mirror of
https://github.com/correl/mage.git
synced 2025-01-11 19:13:02 +00:00
commit
e60d9c2257
63 changed files with 3197 additions and 471 deletions
|
@ -141,6 +141,13 @@
|
|||
<artifactId>balloontip</artifactId>
|
||||
<version>1.2.4.1</version>
|
||||
</dependency>
|
||||
<!-- svg support start -->
|
||||
<dependency>
|
||||
<groupId>batik</groupId>
|
||||
<artifactId>batik-transcoder</artifactId>
|
||||
<version>1.6-1</version>
|
||||
</dependency>
|
||||
<!-- svg support end -->
|
||||
</dependencies>
|
||||
|
||||
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
XMage.de 1 (Europe/Germany) fast :xmage.de:17171
|
||||
woogerworks (North America/USA) :xmage.woogerworks.com:17171
|
||||
xmage.lukeskywalk.com (North America) :xmage.lukeskywalk.com:17171
|
||||
play.xmage.net (North America/Canada) :play.xmage.net:17171
|
||||
XMageBr. (South America/Brazil) :magic.ncs3sistemas.com.br:17171
|
||||
XMage.tahiti :xmage.tahiti.one:443
|
||||
|
|
|
@ -92,6 +92,8 @@ import org.mage.plugins.card.images.DownloadPictures;
|
|||
import org.mage.plugins.card.info.CardInfoPaneImpl;
|
||||
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
||||
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
|
@ -48,6 +48,7 @@ import mage.view.CardView;
|
|||
import mage.view.CardsView;
|
||||
import mage.view.SimpleCardView;
|
||||
import org.mage.card.arcane.CardPanel;
|
||||
import org.mage.card.arcane.ManaSymbolsCellRenderer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
|
@ -164,6 +165,9 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
|
|||
mainTable.getColumnModel().getColumn(6).setPreferredWidth(15);
|
||||
mainTable.getColumnModel().getColumn(7).setPreferredWidth(15);
|
||||
|
||||
// new mana render (svg support)
|
||||
mainTable.getColumnModel().getColumn(mainModel.COLUMN_INDEX_COST).setCellRenderer(new ManaSymbolsCellRenderer());
|
||||
|
||||
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView").equals("listView")) {
|
||||
jToggleListView.setSelected(true);
|
||||
panelCardArea.setViewportView(mainTable);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.client.constants;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.border.Border;
|
||||
|
@ -72,18 +73,45 @@ public final class Constants {
|
|||
|
||||
public static final double SCALE_FACTOR = 0.5;
|
||||
|
||||
public static final String PLUGINS_DIRECTORY = "plugins/";
|
||||
// cards render
|
||||
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
|
||||
public static final Rectangle THUMBNAIL_SIZE_FULL = new Rectangle(102, 146);
|
||||
|
||||
public static final String RESOURCE_PATH_MANA_LARGE = IO.imageBaseDir + "symbols" + File.separator + "large";
|
||||
public static final String RESOURCE_PATH_MANA_MEDIUM = IO.imageBaseDir + "symbols" + File.separator + "medium";
|
||||
public static final String RESOURCE_PATH_SET = IO.imageBaseDir + "sets" + File.separator;
|
||||
public static final String RESOURCE_PATH_SET_SMALL = RESOURCE_PATH_SET + File.separator + "small" + File.separator;
|
||||
public static final String BASE_SOUND_PATH = "sounds" + File.separator;
|
||||
// resources - default images
|
||||
public static final String RESOURCE_PATH_DEFAUL_IMAGES = File.separator + "default";
|
||||
|
||||
// resources - symbols
|
||||
public static final String RESOURCE_PATH_SYMBOLS = File.separator + "symbols";
|
||||
public static final String RESOURCE_SYMBOL_FOLDER_SMALL = "small";
|
||||
public static final String RESOURCE_SYMBOL_FOLDER_MEDIUM = "medium";
|
||||
public static final String RESOURCE_SYMBOL_FOLDER_LARGE = "large";
|
||||
public static final String RESOURCE_SYMBOL_FOLDER_SVG = "svg";
|
||||
public static final String RESOURCE_SYMBOL_FOLDER_PNG = "png";
|
||||
public enum ResourceSymbolSize {
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
LARGE,
|
||||
SVG,
|
||||
PNG
|
||||
}
|
||||
|
||||
// resources - sets
|
||||
public static final String RESOURCE_PATH_SETS = File.separator + "sets";
|
||||
public static final String RESOURCE_SET_FOLDER_SMALL = "small";
|
||||
public static final String RESOURCE_SET_FOLDER_MEDIUM = ""; // empty, medium images laydown in "sets" folder, TODO: delete that and auto gen, use png for html, not gif
|
||||
public static final String RESOURCE_SET_FOLDER_SVG = "svg";
|
||||
public enum ResourceSetSize {
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
SVG
|
||||
}
|
||||
|
||||
// sound
|
||||
public static final String BASE_SOUND_PATH = "sounds" + File.separator; // TODO: check path with File.separator
|
||||
public static final String BASE_MUSICS_PATH = "music" + File.separator;
|
||||
|
||||
public interface IO {
|
||||
|
||||
String imageBaseDir = "plugins" + File.separator + "images" + File.separator;
|
||||
String DEFAULT_IMAGES_DIR = "plugins" + File.separator + "images" + File.separator;
|
||||
String IMAGE_PROPERTIES_FILE = "image.url.properties";
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ import mage.filter.predicate.other.CardTextPredicate;
|
|||
import mage.filter.predicate.other.ExpansionSetPredicate;
|
||||
import mage.view.CardView;
|
||||
import mage.view.CardsView;
|
||||
import org.mage.card.arcane.ManaSymbolsCellRenderer;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -133,6 +134,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
mainTable.getColumnModel().getColumn(6).setPreferredWidth(15);
|
||||
mainTable.getColumnModel().getColumn(7).setPreferredWidth(15);
|
||||
|
||||
// new mana render (svg support)
|
||||
mainTable.getColumnModel().getColumn(mainModel.COLUMN_INDEX_COST).setCellRenderer(new ManaSymbolsCellRenderer());
|
||||
|
||||
// mainTable.setToolTipText(cardSelectorScrollPane.getToolTipText());
|
||||
cardSelectorScrollPane.setViewportView(mainTable);
|
||||
mainTable.setOpaque(false);
|
||||
|
|
|
@ -204,7 +204,7 @@ public class MageBook extends JComponent {
|
|||
if (setImage != null) {
|
||||
tab.setOverlayImage(setImage);
|
||||
} else {
|
||||
System.out.println("Couldn't find symbol image: " + "/plugins/images/sets/" + set + "-C.jpg");
|
||||
System.out.println("Couldn't find symbol image: " + set + "-C.jpg");
|
||||
}
|
||||
tab.setSet(set);
|
||||
tab.setBounds(0, y, 39, 120);
|
||||
|
|
|
@ -79,6 +79,7 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
|
|||
private UpdateCountsCallback updateCountsCallback;
|
||||
|
||||
private final String column[] = {"Qty", "Name", "Cost", "Color", "Type", "Stats", "Rarity", "Set", "#"};
|
||||
public final int COLUMN_INDEX_COST = 2;
|
||||
|
||||
private SortSetting sortSetting;
|
||||
private int recentSortedColumn;
|
||||
|
@ -239,6 +240,10 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
|
|||
case 1:
|
||||
return c.getName();
|
||||
case 2:
|
||||
// new svg images version
|
||||
return ManaSymbols.getStringManaCost(c.getManaCost());
|
||||
/*
|
||||
// old html images version
|
||||
String manaCost = "";
|
||||
for (String m : c.getManaCost()) {
|
||||
manaCost += m;
|
||||
|
@ -246,6 +251,8 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
|
|||
String castingCost = UI.getDisplayManaCost(manaCost);
|
||||
castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, ManaSymbols.Type.TABLE);
|
||||
return "<html>" + castingCost + "</html>";
|
||||
return castingCost;
|
||||
*/
|
||||
case 3:
|
||||
return c.getColorText();
|
||||
case 4:
|
||||
|
|
|
@ -32,9 +32,11 @@ import org.apache.log4j.Logger;
|
|||
import org.mage.plugins.card.CardPluginImpl;
|
||||
import org.mage.plugins.theme.ThemePluginImpl;
|
||||
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
public enum Plugins implements MagePlugins {
|
||||
instance;
|
||||
public static final String PLUGINS_DIRECTORY = "plugins/";
|
||||
public static final String PLUGINS_DIRECTORY = "plugins";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Plugins.class);
|
||||
private static PluginManager pm;
|
||||
|
@ -48,9 +50,10 @@ public enum Plugins implements MagePlugins {
|
|||
|
||||
@Override
|
||||
public void loadPlugins() {
|
||||
|
||||
LOGGER.info("Loading plugins...");
|
||||
pm = PluginManagerFactory.createPluginManager();
|
||||
pm.addPluginsFrom(new File(PLUGINS_DIRECTORY).toURI());
|
||||
pm.addPluginsFrom(new File(PLUGINS_DIRECTORY + File.separator).toURI());
|
||||
this.cardPlugin = new CardPluginImpl();
|
||||
this.counterPlugin = pm.getPlugin(CounterPlugin.class);
|
||||
this.themePlugin = new ThemePluginImpl();
|
||||
|
@ -131,10 +134,8 @@ public enum Plugins implements MagePlugins {
|
|||
|
||||
@Override
|
||||
public void downloadSymbols() {
|
||||
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);
|
||||
if (this.cardPlugin != null) {
|
||||
this.cardPlugin.downloadSymbols(path);
|
||||
this.cardPlugin.downloadSymbols(getImagesDir());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,23 +13,21 @@ import mage.view.CardView;
|
|||
import mage.view.CounterView;
|
||||
import mage.view.PermanentView;
|
||||
import mage.view.StackAbilityView;
|
||||
import net.java.truevfs.access.TFile;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdesktop.swingx.graphics.GraphicsUtilities;
|
||||
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
|
||||
import org.mage.plugins.card.images.ImageCache;
|
||||
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
||||
import mage.client.constants.Constants;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
|
||||
|
||||
/**
|
||||
* Class for drawing the mage card object by using a form based JComponent
|
||||
* approach
|
||||
|
@ -479,8 +477,10 @@ public class CardPanelComponentImpl extends CardPanel {
|
|||
|
||||
if (getShowCastingCost() && !isAnimationPanel() && canShowCardIcons(getCardWidth(), hasImage)) {
|
||||
|
||||
int symbolMarginX = 2; // 2 px between icons
|
||||
|
||||
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
|
||||
int manaWidth = getManaWidth(manaCost);
|
||||
int manaWidth = getManaWidth(manaCost, symbolMarginX);
|
||||
|
||||
// right top corner with margin (sizes from any sample card, length from black border to mana icon)
|
||||
int manaMarginRight = Math.round(22f / 672f * getCardWidth());
|
||||
|
@ -489,25 +489,19 @@ public class CardPanelComponentImpl extends CardPanel {
|
|||
int manaX = getCardXOffset() + getCardWidth() - manaMarginRight - manaWidth;
|
||||
int manaY = getCardYOffset() + manaMarginTop;
|
||||
|
||||
if (hasImage) {
|
||||
// top right corner if have image like real card
|
||||
ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth());
|
||||
} else {
|
||||
// old version - bottom left corner if haven't card
|
||||
// ManaSymbols.draw(g, manaCost, getCardXOffset() + manaMarginRight, getCardYOffset() + getCardHeight() - manaMarginTop - getSymbolWidth(), getSymbolWidth());
|
||||
|
||||
// new version - like a normal image (it's best to view and construct decks)
|
||||
ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth());
|
||||
}
|
||||
ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), Color.black, symbolMarginX);
|
||||
}
|
||||
}
|
||||
|
||||
private int getManaWidth(String manaCost) {
|
||||
private int getManaWidth(String manaCost, int symbolMarginX) {
|
||||
int width = 0;
|
||||
manaCost = manaCost.replace("\\", "");
|
||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
tok.nextToken();
|
||||
if(width != 0) {
|
||||
width += symbolMarginX;
|
||||
}
|
||||
width += getSymbolWidth();
|
||||
}
|
||||
return width;
|
||||
|
@ -654,7 +648,7 @@ public class CardPanelComponentImpl extends CardPanel {
|
|||
final BufferedImage srcImage;
|
||||
if (gameCard.isFaceDown()) {
|
||||
srcImage = getFaceDownImage();
|
||||
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
|
||||
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
|
||||
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
|
||||
} else {
|
||||
srcImage = ImageCache.getThumbnail(gameCard);
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.apache.log4j.Logger;
|
|||
import org.jdesktop.swingx.graphics.GraphicsUtilities;
|
||||
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
|
||||
import org.mage.plugins.card.images.ImageCache;
|
||||
import mage.client.constants.Constants;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
@ -21,8 +22,6 @@ import java.io.File;
|
|||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
|
||||
|
||||
public class CardPanelRenderImpl extends CardPanel {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CardPanelRenderImpl.class);
|
||||
|
@ -341,7 +340,7 @@ public class CardPanelRenderImpl extends CardPanel {
|
|||
// Nothing to do
|
||||
srcImage = null;
|
||||
faceArtSrcImage = null;
|
||||
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
|
||||
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
|
||||
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
|
||||
faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
|
||||
} else {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package org.mage.card.arcane;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
|
@ -16,27 +15,35 @@ import java.nio.file.Paths;
|
|||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
|
||||
import mage.cards.repository.ExpansionRepository;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.ImageHelper;
|
||||
import mage.client.util.gui.BufferedImageBuilder;
|
||||
import org.apache.batik.dom.svg.SVGDOMImplementation;
|
||||
import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.TranscodingHints;
|
||||
import org.apache.batik.transcoder.image.ImageTranscoder;
|
||||
import org.apache.batik.util.SVGConstants;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
|
||||
import mage.client.constants.Constants;
|
||||
import mage.client.constants.Constants.ResourceSymbolSize;
|
||||
import mage.client.constants.Constants.ResourceSetSize;
|
||||
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
public final class ManaSymbols {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ManaSymbols.class);
|
||||
private static final Map<Integer, Map<String, BufferedImage>> manaImages = new HashMap<>();
|
||||
private static boolean smallSymbolsFound = false;
|
||||
private static boolean mediumSymbolsFound = false;
|
||||
|
||||
private static final Map<String, Map<String, Image>> setImages = new HashMap<>();
|
||||
|
||||
|
@ -63,11 +70,46 @@ public final class ManaSymbols {
|
|||
"BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU",
|
||||
"WP", "UP", "BP", "RP", "GP", "X", "C", "E"};
|
||||
|
||||
public static void loadImages() {
|
||||
renameSymbols(getSymbolsPath() + File.separator + "symbols");
|
||||
smallSymbolsFound = loadSymbolsImages(15);
|
||||
mediumSymbolsFound = loadSymbolsImages(25);
|
||||
private static final JLabel labelRender = new JLabel(); // render mana text
|
||||
|
||||
public static void loadImages() {
|
||||
// TODO: delete files rename jpg->gif (it was for backward compatibility for one of the old version?)
|
||||
renameSymbols(getResourceSymbolsPath(ResourceSymbolSize.SMALL));
|
||||
renameSymbols(getResourceSymbolsPath(ResourceSymbolSize.MEDIUM));
|
||||
renameSymbols(getResourceSymbolsPath(ResourceSymbolSize.LARGE));
|
||||
//renameSymbols(getSymbolsPath(ResourceSymbolSize.SVG)); // not need
|
||||
// TODO: remove medium sets files to "medium" folder like symbols above?
|
||||
|
||||
// preload symbol images
|
||||
loadSymbolImages(15);
|
||||
loadSymbolImages(25);
|
||||
loadSymbolImages(50);
|
||||
|
||||
// save symbol images in png for html replacement in texts
|
||||
// you can add bigger size for better quality
|
||||
Map<String, BufferedImage> pngImages = manaImages.get(50);
|
||||
if (pngImages != null){
|
||||
|
||||
File pngPath = new File(getResourceSymbolsPath(ResourceSymbolSize.PNG));
|
||||
if (!pngPath.exists()) {
|
||||
pngPath.mkdirs();
|
||||
}
|
||||
|
||||
for(String symbol: symbols){
|
||||
try
|
||||
{
|
||||
BufferedImage image = pngImages.get(symbol);
|
||||
if (image != null){
|
||||
File newFile = new File(pngPath.getPath() + File.separator + symbol + ".png");
|
||||
ImageIO.write(image, "png", newFile);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
LOGGER.warn("Can't generate png image for symbol:" + symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// preload set images
|
||||
List<String> setCodes = ExpansionRepository.instance.getSetCodes();
|
||||
if (setCodes == null) {
|
||||
// the cards db file is probaly not included in the client. It will be created after the first connect to a server.
|
||||
|
@ -75,9 +117,11 @@ public final class ManaSymbols {
|
|||
return;
|
||||
}
|
||||
for (String set : setCodes) {
|
||||
|
||||
if (withoutSymbols.contains(set)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] codes;
|
||||
if (onlyMythics.contains(set)) {
|
||||
codes = new String[]{"M"};
|
||||
|
@ -88,8 +132,9 @@ public final class ManaSymbols {
|
|||
Map<String, Image> rarityImages = new HashMap<>();
|
||||
setImages.put(set, rarityImages);
|
||||
|
||||
// load medium size
|
||||
for (String rarityCode : codes) {
|
||||
File file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET + set + '-' + rarityCode + ".jpg");
|
||||
File file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + rarityCode + ".jpg");
|
||||
try {
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
int width = image.getWidth(null);
|
||||
|
@ -107,18 +152,19 @@ public final class ManaSymbols {
|
|||
}
|
||||
}
|
||||
|
||||
// generate small size
|
||||
try {
|
||||
File file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL);
|
||||
File file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM));
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
for (String code : codes) {
|
||||
file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL + set + '-' + code + ".png");
|
||||
file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + code + ".png");
|
||||
if (file.exists()) {
|
||||
continue;
|
||||
}
|
||||
file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET + set + '-' + code + ".jpg");
|
||||
file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + code + ".jpg");
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
try {
|
||||
int width = image.getWidth(null);
|
||||
|
@ -130,7 +176,7 @@ public final class ManaSymbols {
|
|||
}
|
||||
Rectangle r = new Rectangle(15 + dx, (int) (height * (15.0f + dx) / width));
|
||||
BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||
File newFile = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL + File.separator + set + '-' + code + ".png");
|
||||
File newFile = new File(getResourceSetsPath(ResourceSetSize.SMALL) + set + '-' + code + ".png");
|
||||
ImageIO.write(resized, "png", newFile);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -144,13 +190,15 @@ public final class ManaSymbols {
|
|||
}
|
||||
}
|
||||
|
||||
// mark loaded images
|
||||
// TODO: delete that code, images draw-show must dynamicly
|
||||
File file;
|
||||
for (String set : ExpansionRepository.instance.getSetCodes()) {
|
||||
file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL);
|
||||
file = new File(getResourceSetsPath(ResourceSetSize.SMALL));
|
||||
if (!file.exists()) {
|
||||
break;
|
||||
}
|
||||
file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL + set + "-C.png");
|
||||
file = new File(getResourceSetsPath(ResourceSetSize.SMALL) + set + "-C.png");
|
||||
try {
|
||||
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
int width = image.getWidth(null);
|
||||
|
@ -161,42 +209,192 @@ public final class ManaSymbols {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean loadSymbolsImages(int size) {
|
||||
boolean fileErrors = false;
|
||||
HashMap<String, BufferedImage> sizedSymbols = new HashMap<>();
|
||||
String resourcePath = Constants.RESOURCE_PATH_MANA_SMALL;
|
||||
if (size > 25) {
|
||||
resourcePath = Constants.RESOURCE_PATH_MANA_LARGE;
|
||||
} else if (size > 15) {
|
||||
resourcePath = Constants.RESOURCE_PATH_MANA_MEDIUM;
|
||||
}
|
||||
for (String symbol : symbols) {
|
||||
File file = new File(getSymbolsPath() + resourcePath + '/' + symbol + ".gif");
|
||||
try {
|
||||
public static BufferedImage loadSVG(File svgFile, int resizeToWidth, int resizeToHeight) throws IOException {
|
||||
|
||||
if (size == 15 || size == 25) {
|
||||
BufferedImage notResized = ImageIO.read(file);
|
||||
sizedSymbols.put(symbol, notResized);
|
||||
} else {
|
||||
Rectangle r = new Rectangle(size, size);
|
||||
//Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||
BufferedImage image = ImageIO.read(file);
|
||||
//BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||
if (image != null) {
|
||||
BufferedImage resized = ImageHelper.getResizedImage(image, r);
|
||||
sizedSymbols.put(symbol, resized);
|
||||
}
|
||||
// load SVG image
|
||||
// base loader code: https://stackoverflow.com/questions/11435671/how-to-get-a-buffererimage-from-a-svg
|
||||
// resize code: https://vibranttechie.wordpress.com/2015/05/15/svg-loading-to-javafx-stage-and-auto-scaling-when-stage-resize/
|
||||
|
||||
final BufferedImage[] imagePointer = new BufferedImage[1];
|
||||
|
||||
// Rendering hints can't be set programatically, so
|
||||
// we override defaults with a temporary stylesheet.
|
||||
// These defaults emphasize quality and precision, and
|
||||
// are more similar to the defaults of other SVG viewers.
|
||||
// SVG documents can still override these defaults.
|
||||
String css = "svg {" +
|
||||
"shape-rendering: geometricPrecision;" +
|
||||
"text-rendering: geometricPrecision;" +
|
||||
"color-rendering: optimizeQuality;" +
|
||||
"image-rendering: optimizeQuality;" +
|
||||
"}";
|
||||
File cssFile = File.createTempFile("batik-default-override-", ".css");
|
||||
FileWriter w = new FileWriter(cssFile);
|
||||
w.write(css);
|
||||
w.close();
|
||||
|
||||
TranscodingHints transcoderHints = new TranscodingHints();
|
||||
|
||||
// resize
|
||||
if(resizeToWidth > 0){
|
||||
transcoderHints.put(ImageTranscoder.KEY_WIDTH, (float)resizeToWidth); //your image width
|
||||
}
|
||||
if(resizeToHeight > 0){
|
||||
transcoderHints.put(ImageTranscoder.KEY_HEIGHT, (float)resizeToHeight); //your image height
|
||||
}
|
||||
|
||||
transcoderHints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE);
|
||||
transcoderHints.put(ImageTranscoder.KEY_DOM_IMPLEMENTATION,
|
||||
SVGDOMImplementation.getDOMImplementation());
|
||||
transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI,
|
||||
SVGConstants.SVG_NAMESPACE_URI);
|
||||
transcoderHints.put(ImageTranscoder.KEY_DOCUMENT_ELEMENT, "svg");
|
||||
transcoderHints.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, cssFile.toURI().toString());
|
||||
|
||||
try {
|
||||
TranscoderInput input = new TranscoderInput(new FileInputStream(svgFile));
|
||||
|
||||
ImageTranscoder t = new ImageTranscoder() {
|
||||
|
||||
@Override
|
||||
public BufferedImage createImage(int w, int h) {
|
||||
return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Error for symbol:" + symbol);
|
||||
|
||||
@Override
|
||||
public void writeImage(BufferedImage image, TranscoderOutput out)
|
||||
throws TranscoderException {
|
||||
imagePointer[0] = image;
|
||||
}
|
||||
};
|
||||
t.setTranscodingHints(transcoderHints);
|
||||
t.transcode(input, null);
|
||||
}
|
||||
catch (TranscoderException ex) {
|
||||
// Requires Java 6
|
||||
ex.printStackTrace();
|
||||
throw new IOException("Couldn't convert " + svgFile);
|
||||
}
|
||||
finally {
|
||||
cssFile.delete();
|
||||
}
|
||||
|
||||
return imagePointer[0];
|
||||
}
|
||||
|
||||
private static File getSymbolFileNameAsSVG(String symbol){
|
||||
return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg");
|
||||
}
|
||||
|
||||
private static BufferedImage loadSymbolAsSVG(String symbol, int resizeToWidth, int resizeToHeight){
|
||||
|
||||
File sourceFile = getSymbolFileNameAsSVG(symbol);
|
||||
return loadSymbolAsSVG(sourceFile, resizeToWidth, resizeToHeight);
|
||||
}
|
||||
|
||||
private static BufferedImage loadSymbolAsSVG(File sourceFile, int resizeToWidth, int resizeToHeight){
|
||||
try{
|
||||
// no need to resize svg (lib already do it on load)
|
||||
return loadSVG(sourceFile, resizeToWidth, resizeToHeight);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Can't load svg symbol: " + sourceFile.getPath());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static File getSymbolFileNameAsGIF(String symbol, int size){
|
||||
|
||||
ResourceSymbolSize needSize = null;
|
||||
if (size <= 15){
|
||||
needSize = ResourceSymbolSize.SMALL;
|
||||
}else if (size <= 25){
|
||||
needSize = ResourceSymbolSize.MEDIUM;
|
||||
} else {
|
||||
needSize = ResourceSymbolSize.LARGE;
|
||||
}
|
||||
|
||||
return new File(getResourceSymbolsPath(needSize) + symbol + ".gif");
|
||||
}
|
||||
|
||||
private static BufferedImage loadSymbolAsGIF(String symbol, int resizeToWidth, int resizeToHeight){
|
||||
File file = getSymbolFileNameAsGIF(symbol, resizeToWidth);
|
||||
return loadSymbolAsGIF(file, resizeToWidth, resizeToHeight);
|
||||
}
|
||||
|
||||
private static BufferedImage loadSymbolAsGIF(File sourceFile, int resizeToWidth, int resizeToHeight){
|
||||
|
||||
BufferedImage image = null;
|
||||
|
||||
try {
|
||||
if ((resizeToWidth == 15) || (resizeToWidth == 25)){
|
||||
// normal size
|
||||
image = ImageIO.read(sourceFile);
|
||||
}else{
|
||||
// resize size
|
||||
image = ImageIO.read(sourceFile);
|
||||
|
||||
if (image != null) {
|
||||
Rectangle r = new Rectangle(resizeToWidth, resizeToHeight);
|
||||
image = ImageHelper.getResizedImage(image, r);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Can't load gif symbol: " + sourceFile.getPath());
|
||||
return null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static boolean loadSymbolImages(int size) {
|
||||
// load all symbols to cash
|
||||
// priority: SVG -> GIF
|
||||
// gif remain for backward compatibility
|
||||
|
||||
boolean fileErrors = false;
|
||||
|
||||
HashMap<String, BufferedImage> sizedSymbols = new HashMap<>();
|
||||
for (String symbol : symbols) {
|
||||
|
||||
BufferedImage image = null;
|
||||
File file = null;
|
||||
|
||||
// svg
|
||||
file = getSymbolFileNameAsSVG(symbol);
|
||||
if (file.exists()) {
|
||||
image = loadSymbolAsSVG(file, size, size);
|
||||
}
|
||||
|
||||
// gif
|
||||
if (image == null) {
|
||||
//LOGGER.info("SVG symbol can't be load: " + file.getPath());
|
||||
|
||||
file = getSymbolFileNameAsGIF(symbol, size);
|
||||
if (file.exists()) {
|
||||
image = loadSymbolAsGIF(file, size, size);
|
||||
}
|
||||
}
|
||||
|
||||
// save
|
||||
if (image != null) {
|
||||
sizedSymbols.put(symbol, image);
|
||||
} else {
|
||||
fileErrors = true;
|
||||
LOGGER.warn("SVG or GIF symbol can't be load: " + symbol);
|
||||
}
|
||||
}
|
||||
|
||||
manaImages.put(size, sizedSymbols);
|
||||
return !fileErrors;
|
||||
}
|
||||
|
||||
private static void renameSymbols(String path) {
|
||||
File file = new File(path);
|
||||
if (!file.exists()){
|
||||
return;
|
||||
}
|
||||
|
||||
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/*.jpg");
|
||||
try {
|
||||
Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>() {
|
||||
|
@ -210,60 +408,184 @@ public final class ManaSymbols {
|
|||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Couldn't rename mana symbols!");
|
||||
LOGGER.error("Couldn't rename mana symbols on " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSymbolsPath() {
|
||||
return getSymbolsPath(false);
|
||||
private static String getResourceSymbolsPath(ResourceSymbolSize needSize){
|
||||
// return real path to symbols (default or user defined)
|
||||
|
||||
String path = CardImageUtils.getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS + File.separator;
|
||||
|
||||
// folder by sizes
|
||||
switch (needSize) {
|
||||
case SMALL:
|
||||
path = path + Constants.RESOURCE_SYMBOL_FOLDER_SMALL;
|
||||
break;
|
||||
case MEDIUM:
|
||||
path = path + Constants.RESOURCE_SYMBOL_FOLDER_MEDIUM;
|
||||
break;
|
||||
case LARGE:
|
||||
path = path + Constants.RESOURCE_SYMBOL_FOLDER_LARGE;
|
||||
break;
|
||||
case SVG:
|
||||
path = path + Constants.RESOURCE_SYMBOL_FOLDER_SVG;
|
||||
break;
|
||||
case PNG:
|
||||
path = path + Constants.RESOURCE_SYMBOL_FOLDER_PNG;
|
||||
break;
|
||||
default:
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"ResourceSymbolSize value is unknown");
|
||||
}
|
||||
|
||||
// fix double separator if size folder is not set
|
||||
while(path.endsWith(File.separator))
|
||||
{
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path + File.separator;
|
||||
}
|
||||
|
||||
private static String getSymbolsPath(boolean forHtmlCode) {
|
||||
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);
|
||||
if (path == null) {
|
||||
if (forHtmlCode) {
|
||||
// for html code we need to use double '//' symbols
|
||||
// and seems it should be hard coded - as it is not the same as using File.separator
|
||||
return "plugins/images/";
|
||||
} else {
|
||||
return mage.client.constants.Constants.IO.imageBaseDir;
|
||||
}
|
||||
private static String getResourceSetsPath(ResourceSetSize needSize){
|
||||
// return real path to sets icons (default or user defined)
|
||||
|
||||
String path = CardImageUtils.getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS + File.separator;
|
||||
|
||||
// folder by sizes
|
||||
switch (needSize) {
|
||||
case SMALL:
|
||||
path = path + Constants.RESOURCE_SET_FOLDER_SMALL;
|
||||
break;
|
||||
case MEDIUM:
|
||||
path = path + Constants.RESOURCE_SET_FOLDER_MEDIUM;
|
||||
break;
|
||||
case SVG:
|
||||
path = path + Constants.RESOURCE_SET_FOLDER_SVG;
|
||||
break;
|
||||
default:
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"ResourceSetSize value is unknown");
|
||||
}
|
||||
if (forHtmlCode) {
|
||||
if (cachedPath != null) {
|
||||
return cachedPath;
|
||||
}
|
||||
if (path.contains("\\")) {
|
||||
cachedPath = path.replaceAll("[\\\\]", "/");
|
||||
return cachedPath;
|
||||
}
|
||||
|
||||
// fix double separator if size folder is not set
|
||||
while(path.endsWith(File.separator))
|
||||
{
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
return path;
|
||||
|
||||
return path + File.separator;
|
||||
}
|
||||
|
||||
public static void draw(Graphics g, String manaCost, int x, int y, int symbolWidth) {
|
||||
draw(g, manaCost, x, y, symbolWidth, Color.white, 0);
|
||||
}
|
||||
|
||||
public static void draw(Graphics g, String manaCost, int x, int y, int symbolWidth, Color symbolsTextColor, int symbolMarginX) {
|
||||
if (!manaImages.containsKey(symbolWidth)) {
|
||||
loadSymbolsImages(symbolWidth);
|
||||
loadSymbolImages(symbolWidth);
|
||||
}
|
||||
|
||||
// TODO: replace with jlabel render (look at table rendere)?
|
||||
|
||||
/*
|
||||
// NEW version with component draw
|
||||
JPanel manaPanel = new JPanel();
|
||||
|
||||
// icons size with margin
|
||||
int symbolHorizontalMargin = 2;
|
||||
|
||||
// create each mana symbol as child label
|
||||
manaPanel.removeAll();
|
||||
manaPanel.setLayout(new BoxLayout(manaPanel, BoxLayout.X_AXIS));
|
||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String symbol = tok.nextToken();
|
||||
|
||||
JLabel symbolLabel = new JLabel();
|
||||
//symbolLabel.setBorder(new LineBorder(new Color(150, 150, 150))); // debug
|
||||
symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin,0, 0));
|
||||
|
||||
BufferedImage image = ManaSymbols.getSizedManaSymbol(symbol, symbolWidth);
|
||||
if (image != null){
|
||||
// icon
|
||||
symbolLabel.setIcon(new ImageIcon(image));
|
||||
}else
|
||||
{
|
||||
// text
|
||||
symbolLabel.setText("{" + symbol + "}");
|
||||
//symbolLabel.setOpaque(baseLabel.isOpaque());
|
||||
//symbolLabel.setForeground(baseLabel.getForeground());
|
||||
//symbolLabel.setBackground(baseLabel.getBackground());
|
||||
}
|
||||
|
||||
manaPanel.add(symbolLabel);
|
||||
}
|
||||
|
||||
// draw result
|
||||
Dimension d = manaPanel.getPreferredSize();
|
||||
BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D gg = image.createGraphics();
|
||||
manaPanel.paint(gg);
|
||||
g.drawImage(image, x, y, null);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// OLD version with custom draw
|
||||
Map<String, BufferedImage> sizedSymbols = manaImages.get(symbolWidth);
|
||||
if (manaCost.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
manaCost = manaCost.replace("\\", "");
|
||||
manaCost = UI.getDisplayManaCost(manaCost);
|
||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String symbol = tok.nextToken();
|
||||
// Check and load symbol in the width
|
||||
Image image = sizedSymbols.get(symbol);
|
||||
|
||||
if (image == null) {
|
||||
//log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost);
|
||||
continue;
|
||||
// TEXT draw
|
||||
|
||||
labelRender.setText("{" + symbol + "}");
|
||||
labelRender.setForeground(symbolsTextColor);
|
||||
labelRender.setSize(symbolWidth, symbolWidth);
|
||||
labelRender.setVerticalAlignment(SwingConstants.CENTER);
|
||||
labelRender.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
//labelRender.setBorder(new LineBorder(new Color(125, 250, 250), 1));
|
||||
|
||||
// fix font size for mana text
|
||||
// work for labels WITHOUT borders
|
||||
// https://stackoverflow.com/questions/2715118/how-to-change-the-size-of-the-font-of-a-jlabel-to-take-the-maximum-size
|
||||
Font labelFont = labelRender.getFont();
|
||||
String labelText = "{W}"; //labelRender.getText(); // need same font size for all -- use max symbol ever, not current text
|
||||
int stringWidth = labelRender.getFontMetrics(labelFont).stringWidth(labelText);
|
||||
int componentWidth = labelRender.getWidth();
|
||||
// Find out how much the font can grow in width.
|
||||
double widthRatio = (double)componentWidth / (double)stringWidth;
|
||||
int newFontSize = (int)(labelFont.getSize() * widthRatio);
|
||||
int componentHeight = labelRender.getHeight();
|
||||
// Pick a new font size so it will not be larger than the height of label.
|
||||
int fontSizeToUse = Math.min(newFontSize, componentHeight);
|
||||
// Set the label's font size to the newly determined size.
|
||||
labelRender.setFont(new Font(labelFont.getName(), Font.PLAIN + Font.BOLD, fontSizeToUse - 1)); // - for "..." fix in text
|
||||
|
||||
// render component to new position
|
||||
// need to copy graphics, overvise it draw at top left corner
|
||||
// https://stackoverflow.com/questions/4974268/java-paint-problem
|
||||
Graphics2D labelG = (Graphics2D)g.create(x, y, symbolWidth, symbolWidth);
|
||||
labelG.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
labelG.fillOval(x + 1, y + 1, symbolWidth - 2, symbolWidth - 2);
|
||||
labelRender.paint(labelG);
|
||||
}else {
|
||||
// ICON draw
|
||||
g.drawImage(image, x, y, null);
|
||||
}
|
||||
g.drawImage(image, x, y, null);
|
||||
x += symbolWidth;
|
||||
x += symbolWidth + symbolMarginX;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getStringManaCost(List<String> manaCost) {
|
||||
|
@ -281,11 +603,21 @@ public final class ManaSymbols {
|
|||
TOOLTIP,
|
||||
}
|
||||
|
||||
private static String filePathToUrl(String path){
|
||||
// convert file path to uri path (for html docs)
|
||||
if((path != null) && (!path.equals(""))){
|
||||
File file = new File(path);
|
||||
return file.toURI().toString();
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized String replaceSymbolsWithHTML(String value, Type type) {
|
||||
value = value.replace("{source}", "|source|");
|
||||
value = value.replace("{this}", "|this|");
|
||||
String replaced = value;
|
||||
boolean symbolFilesFound;
|
||||
|
||||
// mana cost to HTML images (urls to files)
|
||||
// do not use it for new code - try to suppotr svg render
|
||||
|
||||
int symbolSize;
|
||||
switch (type) {
|
||||
case TABLE:
|
||||
|
@ -304,21 +636,37 @@ public final class ManaSymbols {
|
|||
symbolSize = 11;
|
||||
break;
|
||||
}
|
||||
String resourcePath = "small";
|
||||
symbolFilesFound = smallSymbolsFound;
|
||||
if (symbolSize > 25) {
|
||||
resourcePath = "large";
|
||||
} else if (symbolSize > 15) {
|
||||
resourcePath = "medium";
|
||||
symbolFilesFound = mediumSymbolsFound;
|
||||
}
|
||||
if (symbolFilesFound) {
|
||||
replaced = REPLACE_SYMBOLS_PATTERN.matcher(value).replaceAll(
|
||||
"<img src='file:" + getSymbolsPath(true) + "/symbols/" + resourcePath + "/$1$2.gif' alt='$1$2' width=" + symbolSize
|
||||
+ " height=" + symbolSize + '>');
|
||||
|
||||
// auto size
|
||||
ResourceSymbolSize needSize = null;
|
||||
if (symbolSize <= 15){
|
||||
needSize = ResourceSymbolSize.SMALL;
|
||||
}else if (symbolSize <= 25){
|
||||
needSize = ResourceSymbolSize.MEDIUM;
|
||||
} else {
|
||||
needSize = ResourceSymbolSize.LARGE;
|
||||
}
|
||||
|
||||
// replace every {symbol} to <img> link
|
||||
|
||||
// ignore data backup
|
||||
String replaced = value
|
||||
.replace("{source}", "|source|")
|
||||
.replace("{this}", "|this|");
|
||||
|
||||
|
||||
// not need to add different images (width and height do the work)
|
||||
// use best png size (generated on startup) TODO: add reload images after update
|
||||
String htmlImagesPath = getResourceSymbolsPath(ResourceSymbolSize.PNG);
|
||||
|
||||
replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll(
|
||||
"<img src='" + filePathToUrl(htmlImagesPath) + "$1$2" + ".png' alt='$1$2' width="
|
||||
+ symbolSize + " height=" + symbolSize + '>');
|
||||
|
||||
// ignore data restore
|
||||
replaced = replaced.replace("|source|", "{source}");
|
||||
replaced = replaced.replace("|this|", "{this}");
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
|
@ -328,7 +676,7 @@ public final class ManaSymbols {
|
|||
int factor = size / 15 + 1;
|
||||
Integer width = setImagesExist.get(_set).width * factor;
|
||||
Integer height = setImagesExist.get(_set).height * factor;
|
||||
return "<img src='file:" + getSymbolsPath() + "/sets/small/" + _set + '-' + rarity + ".png' alt='" + rarity + "' height='" + height + "' width='" + width + "' >";
|
||||
return "<img src='" + filePathToUrl(getResourceSetsPath(ResourceSetSize.SMALL)) + _set + '-' + rarity + ".png' alt='" + rarity + "' height='" + height + "' width='" + width + "' >";
|
||||
} else {
|
||||
return set;
|
||||
}
|
||||
|
@ -353,9 +701,10 @@ public final class ManaSymbols {
|
|||
|
||||
public static BufferedImage getSizedManaSymbol(String symbol, int size) {
|
||||
if (!manaImages.containsKey(size)) {
|
||||
loadSymbolsImages(size);
|
||||
loadSymbolImages(size);
|
||||
}
|
||||
Map<String, BufferedImage> sizedSymbols = manaImages.get(size);
|
||||
return sizedSymbols.get(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package org.mage.card.arcane;
|
||||
|
||||
import mage.client.util.GUISizeHelper;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public final class ManaSymbolsCellRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
// base panel to render
|
||||
private JPanel manaPanel = new JPanel();
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
||||
boolean hasFocus, int row, int column) {
|
||||
|
||||
// get table text cell settings
|
||||
DefaultTableCellRenderer baseRenderer = (DefaultTableCellRenderer) table.getDefaultRenderer(String.class);
|
||||
JLabel baseLabel = (JLabel)baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
// apply settings to mana panel from parent
|
||||
manaPanel.setOpaque(baseLabel.isOpaque());
|
||||
manaPanel.setForeground(baseLabel.getForeground());
|
||||
manaPanel.setBackground(baseLabel.getBackground());
|
||||
|
||||
// icons size with margin
|
||||
int symbolWidth = GUISizeHelper.symbolTableSize;
|
||||
int symbolHorizontalMargin = 2;
|
||||
|
||||
// create each mana symbol as child label
|
||||
String manaCost = (String)value;
|
||||
manaPanel.removeAll();
|
||||
manaPanel.setLayout(new BoxLayout(manaPanel, BoxLayout.X_AXIS));
|
||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String symbol = tok.nextToken();
|
||||
|
||||
JLabel symbolLabel = new JLabel();
|
||||
//symbolLabel.setBorder(new LineBorder(new Color(150, 150, 150))); // debug
|
||||
symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin,0, 0));
|
||||
|
||||
BufferedImage image = ManaSymbols.getSizedManaSymbol(symbol, symbolWidth);
|
||||
if (image != null){
|
||||
// icon
|
||||
symbolLabel.setIcon(new ImageIcon(image));
|
||||
}else
|
||||
{
|
||||
// text
|
||||
symbolLabel.setText("{" + symbol + "}");
|
||||
symbolLabel.setOpaque(baseLabel.isOpaque());
|
||||
symbolLabel.setForeground(baseLabel.getForeground());
|
||||
symbolLabel.setBackground(baseLabel.getBackground());
|
||||
}
|
||||
|
||||
manaPanel.add(symbolLabel);
|
||||
}
|
||||
|
||||
return manaPanel;
|
||||
}
|
||||
}
|
|
@ -561,7 +561,7 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
|
||||
// Draw the mana symbols
|
||||
if (!cardView.isAbility() && !cardView.isFaceDown()) {
|
||||
ManaSymbols.draw(g, manaCost, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight);
|
||||
ManaSymbols.draw(g, manaCost, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight, Color.black, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -844,7 +844,7 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
String symbs = symbol;
|
||||
int symbHeight = (int) (0.8 * h);
|
||||
int manaCostWidth = CardRendererUtils.getManaCostWidth(symbs, symbHeight);
|
||||
ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight);
|
||||
ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight, Color.black, 2);
|
||||
}
|
||||
|
||||
// Get the first line of the textbox, the keyword string
|
||||
|
|
|
@ -6,7 +6,6 @@ import mage.client.dialog.PreferencesDialog;
|
|||
import mage.client.util.GUISizeHelper;
|
||||
import mage.constants.Rarity;
|
||||
import mage.interfaces.plugin.CardPlugin;
|
||||
import mage.utils.CardUtil;
|
||||
import mage.view.CardView;
|
||||
import mage.view.CounterView;
|
||||
import mage.view.PermanentView;
|
||||
|
@ -19,7 +18,6 @@ import org.mage.card.arcane.*;
|
|||
import org.mage.plugins.card.dl.DownloadGui;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import org.mage.plugins.card.dl.Downloader;
|
||||
import org.mage.plugins.card.dl.sources.CardFrames;
|
||||
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
|
||||
import org.mage.plugins.card.dl.sources.GathererSets;
|
||||
import org.mage.plugins.card.dl.sources.GathererSymbols;
|
||||
|
@ -525,30 +523,30 @@ public class CardPluginImpl implements CardPlugin {
|
|||
/**
|
||||
* Download various symbols (mana, tap, set).
|
||||
*
|
||||
* @param imagesPath Path to check in and store symbols to. Can be null, in
|
||||
* such case default path should be used.
|
||||
* @param imagesDir Path to check in and store symbols to. Can't be null.
|
||||
*/
|
||||
@Override
|
||||
public void downloadSymbols(String imagesPath) {
|
||||
public void downloadSymbols(String imagesDir) {
|
||||
final DownloadGui g = new DownloadGui(new Downloader());
|
||||
|
||||
Iterable<DownloadJob> it = new GathererSymbols(imagesPath);
|
||||
|
||||
Iterable<DownloadJob> it = new GathererSymbols();
|
||||
for (DownloadJob job : it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
|
||||
it = new GathererSets(imagesPath);
|
||||
it = new GathererSets();
|
||||
for (DownloadJob job : it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
|
||||
it = new CardFrames(imagesPath);
|
||||
/*
|
||||
it = new CardFrames(imagesDir); // TODO: delete frames download (not need now)
|
||||
for (DownloadJob job : it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
*/
|
||||
|
||||
it = new DirectLinksForDownload(imagesPath);
|
||||
it = new DirectLinksForDownload();
|
||||
for (DownloadJob job : it) {
|
||||
g.getDownloader().add(job);
|
||||
}
|
||||
|
@ -560,6 +558,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
public void windowClosing(WindowEvent e) {
|
||||
g.getDownloader().dispose();
|
||||
ManaSymbols.loadImages();
|
||||
// TODO: check reload process after download (icons do not update)
|
||||
}
|
||||
});
|
||||
d.setLayout(new BorderLayout());
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package org.mage.plugins.card.constants;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.io.File;
|
||||
|
||||
public final class Constants {
|
||||
|
||||
public static final String RESOURCE_PATH_SET = File.separator + "sets" + File.separator;
|
||||
|
||||
public static final String RESOURCE_PATH_MANA_SMALL = File.separator + "symbols" + File.separator + "small";
|
||||
public static final String RESOURCE_PATH_MANA_LARGE = File.separator + "symbols" + File.separator + "large";
|
||||
public static final String RESOURCE_PATH_MANA_MEDIUM = File.separator + "symbols" + File.separator + "medium";
|
||||
|
||||
public static final String RESOURCE_PATH_SET_SMALL = RESOURCE_PATH_SET + File.separator + "small" + File.separator;
|
||||
|
||||
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
|
||||
public static final Rectangle THUMBNAIL_SIZE_FULL = new Rectangle(102, 146);
|
||||
|
||||
public interface IO {
|
||||
|
||||
String imageBaseDir = "plugins" + File.separator + "images";
|
||||
String IMAGE_PROPERTIES_FILE = "image.url.properties";
|
||||
}
|
||||
|
||||
public static final String CARD_IMAGE_PATH_TEMPLATE = '.' + File.separator + "plugins" + File.separator + "images/{set}/{name}.{collector}.full.jpg";
|
||||
}
|
|
@ -11,9 +11,12 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mage.client.constants.Constants;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* Used when we need to point to direct links to download resources from.
|
||||
|
@ -33,15 +36,13 @@ public class DirectLinksForDownload implements Iterable<DownloadJob> {
|
|||
directLinks.put(cardbackFilename, backsideUrl);
|
||||
}
|
||||
|
||||
private static final String DEFAULT_IMAGES_PATH = File.separator + "default";
|
||||
private static final File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + DEFAULT_IMAGES_PATH);
|
||||
public static File outDir = DEFAULT_OUT_DIR;
|
||||
public static File outDir;
|
||||
|
||||
public DirectLinksForDownload(String path) {
|
||||
if (path == null) {
|
||||
useDefaultDir();
|
||||
} else {
|
||||
changeOutDir(path);
|
||||
public DirectLinksForDownload() {
|
||||
outDir = new File(getImagesDir() + Constants.RESOURCE_PATH_DEFAUL_IMAGES);
|
||||
|
||||
if (!outDir.exists()){
|
||||
outDir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,20 +56,4 @@ public class DirectLinksForDownload implements Iterable<DownloadJob> {
|
|||
}
|
||||
return jobs.iterator();
|
||||
}
|
||||
|
||||
private void changeOutDir(String path) {
|
||||
File file = new File(path + DEFAULT_IMAGES_PATH);
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
} else {
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useDefaultDir() {
|
||||
outDir = DEFAULT_OUT_DIR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,14 @@ import java.util.Iterator;
|
|||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.Sets;
|
||||
import mage.client.constants.Constants;
|
||||
import mage.constants.Rarity;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class GathererSets implements Iterable<DownloadJob> {
|
||||
|
@ -36,13 +39,11 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
}
|
||||
}
|
||||
|
||||
private static File outDir;
|
||||
|
||||
private static final int DAYS_BEFORE_RELEASE_TO_DOWNLOAD = +14; // Try to load the symbolsBasic eralies 14 days before release date
|
||||
private static final Logger logger = Logger.getLogger(GathererSets.class);
|
||||
|
||||
private static final String SETS_PATH = File.separator + "sets";
|
||||
private static final File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SETS_PATH);
|
||||
private static File outDir = DEFAULT_OUT_DIR;
|
||||
|
||||
private static final String[] symbolsBasic = {"10E", "9ED", "8ED", "7ED", "6ED", "5ED", "4ED", "3ED", "2ED", "LEB", "LEA",
|
||||
"HOP",
|
||||
"ARN", "ATQ", "LEG", "DRK", "FEM", "HML",
|
||||
|
@ -150,11 +151,12 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
codeReplacements.put("CHR", "CH");
|
||||
}
|
||||
|
||||
public GathererSets(String path) {
|
||||
if (path == null) {
|
||||
useDefaultDir();
|
||||
} else {
|
||||
changeOutDir(path);
|
||||
public GathererSets() {
|
||||
|
||||
outDir = new File(getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS);
|
||||
|
||||
if (!outDir.exists()){
|
||||
outDir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,20 +313,4 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
|
||||
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst));
|
||||
}
|
||||
|
||||
private void changeOutDir(String path) {
|
||||
File file = new File(path + SETS_PATH);
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
} else {
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useDefaultDir() {
|
||||
outDir = DEFAULT_OUT_DIR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,12 @@ import com.google.common.collect.AbstractIterator;
|
|||
import java.io.File;
|
||||
import static java.lang.String.format;
|
||||
import java.util.Iterator;
|
||||
|
||||
import mage.client.constants.Constants;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* The class GathererSymbols.
|
||||
|
@ -23,9 +26,7 @@ public class GathererSymbols implements Iterable<DownloadJob> {
|
|||
//TODO chaos and planeswalker symbol
|
||||
//chaos: http://gatherer.wizards.com/Images/Symbols/chaos.gif
|
||||
|
||||
private static final String SYMBOLS_PATH = File.separator + "symbols";
|
||||
private static final File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SYMBOLS_PATH);
|
||||
private static File outDir = DEFAULT_OUT_DIR;
|
||||
private static File outDir;
|
||||
|
||||
private static final String urlFmt = "http://gatherer.wizards.com/handlers/image.ashx?size=%1$s&name=%2$s&type=symbol";
|
||||
|
||||
|
@ -38,11 +39,11 @@ public class GathererSymbols implements Iterable<DownloadJob> {
|
|||
"X", "S", "T", "Q", "C", "E"};
|
||||
private static final int minNumeric = 0, maxNumeric = 16;
|
||||
|
||||
public GathererSymbols(String path) {
|
||||
if (path == null) {
|
||||
useDefaultDir();
|
||||
} else {
|
||||
changeOutDir(path);
|
||||
public GathererSymbols() {
|
||||
outDir = new File(getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS);
|
||||
|
||||
if (!outDir.exists()){
|
||||
outDir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,20 +116,4 @@ public class GathererSymbols implements Iterable<DownloadJob> {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void changeOutDir(String path) {
|
||||
File file = new File(path + SYMBOLS_PATH);
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
} else {
|
||||
file.mkdirs();
|
||||
if (file.exists()) {
|
||||
outDir = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void useDefaultDir() {
|
||||
outDir = DEFAULT_OUT_DIR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,9 @@ import mage.cards.repository.CardCriteria;
|
|||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.constants.Constants;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.util.sets.ConstructedFormats;
|
||||
import mage.remote.Connection;
|
||||
import static mage.remote.Connection.ProxyType.HTTP;
|
||||
import static mage.remote.Connection.ProxyType.NONE;
|
||||
import static mage.remote.Connection.ProxyType.SOCKS;
|
||||
import net.java.truevfs.access.TFile;
|
||||
import net.java.truevfs.access.TFileOutputStream;
|
||||
import net.java.truevfs.access.TVFS;
|
||||
|
@ -43,6 +39,8 @@ import org.mage.plugins.card.dl.sources.*;
|
|||
import org.mage.plugins.card.properties.SettingsManager;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
|
||||
|
||||
private static DownloadPictures instance;
|
||||
|
@ -462,7 +460,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
*/
|
||||
List<CardDownloadData> cardsToDownload = Collections.synchronizedList(new ArrayList<>());
|
||||
allCardsUrls.parallelStream().forEach(card -> {
|
||||
TFile file = new TFile(CardImageUtils.generateImagePath(card));
|
||||
File file = new TFile(CardImageUtils.buildImagePathToCard(card));
|
||||
logger.debug(card.getName() + " (is_token=" + card.isToken() + "). Image is here:" + file.getAbsolutePath() + " (exists=" + file.exists() + ')');
|
||||
if (!file.exists()) {
|
||||
logger.debug("Missing: " + file.getAbsolutePath());
|
||||
|
@ -544,7 +542,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
public void run() {
|
||||
this.cardIndex = 0;
|
||||
|
||||
File base = new File(Constants.IO.imageBaseDir);
|
||||
File base = new File(getImagesDir());
|
||||
if (!base.exists()) {
|
||||
base.mkdir();
|
||||
}
|
||||
|
@ -678,38 +676,78 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
File temporaryFile = null;
|
||||
TFile outputFile = null;
|
||||
if (cancel) {
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TFile fileTempImage;
|
||||
TFile destFile;
|
||||
try {
|
||||
filePath.append(Constants.IO.imageBaseDir);
|
||||
if (!useSpecifiedPaths && card != null) {
|
||||
filePath.append(card.hashCode()).append('.').append(card.getName().replace(":", "").replace("//", "-")).append(".jpg");
|
||||
temporaryFile = new File(filePath.toString());
|
||||
}
|
||||
String imagePath;
|
||||
if (useSpecifiedPaths) {
|
||||
if (card != null && card.isToken()) {
|
||||
imagePath = CardImageUtils.getTokenBasePath() + actualFilename;
|
||||
} else if (card != null) {
|
||||
imagePath = CardImageUtils.getImageBasePath() + actualFilename;
|
||||
} else {
|
||||
imagePath = Constants.IO.imageBaseDir;
|
||||
}
|
||||
|
||||
String tmpFile = filePath + "temporary" + actualFilename;
|
||||
temporaryFile = new File(tmpFile);
|
||||
if (!temporaryFile.exists()) {
|
||||
temporaryFile.getParentFile().mkdirs();
|
||||
if (card == null){
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
} else {
|
||||
imagePath = CardImageUtils.generateImagePath(card);
|
||||
return;
|
||||
}
|
||||
|
||||
outputFile = new TFile(imagePath);
|
||||
if (!outputFile.exists()) {
|
||||
outputFile.getParentFile().mkdirs();
|
||||
// gen temp file (download to images folder)
|
||||
String tempPath = getImagesDir() + File.separator + "downloading" + File.separator;
|
||||
if(useSpecifiedPaths){
|
||||
fileTempImage = new TFile(tempPath + actualFilename + "-" + card.hashCode() + ".jpg");
|
||||
}else{
|
||||
fileTempImage = new TFile(tempPath + CardImageUtils.prepareCardNameForFile(card.getName()) + "-" + card.hashCode() + ".jpg");
|
||||
}
|
||||
if(!fileTempImage.getParentFile().exists()){
|
||||
fileTempImage.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
// gen dest file name
|
||||
if(useSpecifiedPaths)
|
||||
{
|
||||
if(card.isToken()){
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToSet(card) + actualFilename + ".jpg");
|
||||
}else{
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToTokens() + actualFilename + ".jpg");
|
||||
}
|
||||
}else{
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToCard(card));
|
||||
}
|
||||
|
||||
// FILE already exists (in zip or in dir)
|
||||
if (destFile.exists()){
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// zip can't be read
|
||||
TFile testArchive = destFile.getTopLevelArchive();
|
||||
if (testArchive != null && testArchive.exists()) {
|
||||
try {
|
||||
testArchive.list();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error reading archive, may be it was corrapted. Try to delete it: " + testArchive.toString());
|
||||
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(!destFile.getParentFile().exists()){
|
||||
destFile.getParentFile().mkdirs();
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// WTF start?! TODO: wtf
|
||||
File existingFile = new File(imagePath.replaceFirst("\\w{3}.zip", ""));
|
||||
if (existingFile.exists()) {
|
||||
try {
|
||||
|
@ -727,7 +765,86 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
}
|
||||
return;
|
||||
}
|
||||
// WTF end?!
|
||||
*/
|
||||
|
||||
|
||||
// START to download
|
||||
cardImageSource.doPause(url.getPath());
|
||||
URLConnection httpConn = url.openConnection(p);
|
||||
httpConn.connect();
|
||||
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
|
||||
|
||||
if (responseCode == 200){
|
||||
// download OK
|
||||
// save data to temp
|
||||
BufferedOutputStream out;
|
||||
try (BufferedInputStream in = new BufferedInputStream(httpConn.getInputStream())) {
|
||||
out = new BufferedOutputStream(new TFileOutputStream(fileTempImage));
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
// user cancelled
|
||||
if (cancel) {
|
||||
in.close();
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
// stop download, save current state and exit
|
||||
TFile archive = destFile.getTopLevelArchive();
|
||||
///* not need to unmout/close - it's auto action
|
||||
if (archive != null && archive.exists()){
|
||||
logger.info("User canceled download. Closing archive file: " + destFile.toString());
|
||||
try {
|
||||
TVFS.umount(archive);
|
||||
}catch (Exception e) {
|
||||
logger.error("Can't close archive file: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}//*/
|
||||
try {
|
||||
TFile.rm(fileTempImage);
|
||||
}catch (Exception e) {
|
||||
logger.error("Can't delete temp file: " + e.getMessage(), e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
// TODO: remove to finnaly section?
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
// TODO: add two faces card correction? (WTF)
|
||||
|
||||
// SAVE final data
|
||||
if (fileTempImage.exists()) {
|
||||
if (!destFile.getParentFile().exists()){
|
||||
destFile.getParentFile().mkdirs();
|
||||
}
|
||||
new TFile(fileTempImage).cp_rp(destFile);
|
||||
try {
|
||||
TFile.rm(fileTempImage);
|
||||
}catch (Exception e) {
|
||||
logger.error("Can't delete temp file: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
// download ERROR
|
||||
logger.warn("Image download for " + card.getName()
|
||||
+ (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
|
||||
+ '(' + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString()
|
||||
);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Shows the returned html from the request to the web server
|
||||
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Logger.getLogger(this.getClass()).info(url.toString());
|
||||
boolean useTempFile = false;
|
||||
int responseCode = 0;
|
||||
|
@ -736,7 +853,6 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
if (temporaryFile != null && temporaryFile.length() > 100) {
|
||||
useTempFile = true;
|
||||
} else {
|
||||
|
||||
cardImageSource.doPause(url.getPath());
|
||||
httpConn = url.openConnection(p);
|
||||
httpConn.connect();
|
||||
|
@ -768,6 +884,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
out.close();
|
||||
}
|
||||
|
||||
// TODO: WTF?! start
|
||||
if (card != null && card.isTwoFacedCard()) {
|
||||
BufferedImage image = ImageIO.read(temporaryFile);
|
||||
if (image.getHeight() == 470) {
|
||||
|
@ -790,6 +907,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
outputFile.getParentFile().mkdirs();
|
||||
new TFile(temporaryFile).cp_rp(outputFile);
|
||||
}
|
||||
// WTF?! end
|
||||
} else {
|
||||
if (card != null && !useSpecifiedPaths) {
|
||||
logger.warn("Image download for " + card.getName()
|
||||
|
@ -800,16 +918,15 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
logger.error("The file " + (outputFile != null ? outputFile.toString() : "to add the image of " + card.getName() + '(' + card.getSet() + ')') + " can't be accessed. Try rebooting your system to remove the file lock.");
|
||||
logger.error("Can't access to files: " + card.getName() + "(" + card.getSet() + "). Try rebooting your system to remove the file lock.");
|
||||
} catch (Exception e) {
|
||||
logger.error(e, e);
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
if (temporaryFile != null) {
|
||||
temporaryFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
|
@ -823,7 +940,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
iwp.setCompressionQuality(0.96f);
|
||||
|
||||
File tempFile = new File(Constants.IO.imageBaseDir + File.separator + image.hashCode() + file.getName());
|
||||
File tempFile = new File(getImagesDir() + File.separator + image.hashCode() + file.getName());
|
||||
FileImageOutputStream output = new FileImageOutputStream(tempFile);
|
||||
writer.setOutput(output);
|
||||
IIOImage image2 = new IIOImage(image, null, null);
|
||||
|
@ -846,7 +963,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
} else {
|
||||
List<CardDownloadData> remainingCards = Collections.synchronizedList(new ArrayList<>());
|
||||
DownloadPictures.this.allCardsMissingImage.parallelStream().forEach(cardDownloadData -> {
|
||||
TFile file = new TFile(CardImageUtils.generateImagePath(cardDownloadData));
|
||||
TFile file = new TFile(CardImageUtils.buildImagePathToCard(cardDownloadData));
|
||||
if (!file.exists()) {
|
||||
remainingCards.add(cardDownloadData);
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ import net.java.truevfs.access.TFile;
|
|||
import net.java.truevfs.access.TFileInputStream;
|
||||
import net.java.truevfs.access.TFileOutputStream;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
import mage.client.constants.Constants;
|
||||
|
||||
/**
|
||||
* This class stores ALL card images in a cache with soft values. this means
|
||||
|
@ -94,7 +94,7 @@ public final class ImageCache {
|
|||
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback
|
||||
}
|
||||
} else {
|
||||
path = CardImageUtils.generateImagePath(info);
|
||||
path = CardImageUtils.buildImagePathToCard(info);
|
||||
}
|
||||
|
||||
if (path == null) {
|
||||
|
@ -263,7 +263,7 @@ public final class ImageCache {
|
|||
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback
|
||||
}
|
||||
} else {
|
||||
path = CardImageUtils.generateImagePath(info);
|
||||
path = CardImageUtils.buildImagePathToCard(info);
|
||||
}
|
||||
|
||||
if (thumbnail && path.endsWith(".jpg")) {
|
||||
|
@ -340,7 +340,7 @@ public final class ImageCache {
|
|||
// image draw to buffer
|
||||
gg.setComposite(AlphaComposite.SrcAtop);
|
||||
gg.drawImage(image, 0, 0, null);
|
||||
//gg.dispose();
|
||||
gg.dispose();
|
||||
|
||||
return cornerImage;
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import org.mage.plugins.card.constants.Constants;
|
||||
import mage.client.constants.Constants;
|
||||
|
||||
public class SettingsManager {
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mage.plugins.card.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.HashMap;
|
||||
|
@ -63,7 +64,7 @@ public final class CardImageUtils {
|
|||
}
|
||||
|
||||
private static String getTokenImagePath(CardDownloadData card) {
|
||||
String filename = generateImagePath(card);
|
||||
String filename = buildImagePathToCard(card);
|
||||
|
||||
TFile file = new TFile(filename);
|
||||
if (!file.exists()) {
|
||||
|
@ -74,12 +75,12 @@ public final class CardImageUtils {
|
|||
if (!file.exists()) {
|
||||
CardDownloadData updated = new CardDownloadData(card);
|
||||
updated.setName(card.getName() + " 1");
|
||||
filename = generateImagePath(updated);
|
||||
filename = buildImagePathToCard(updated);
|
||||
file = new TFile(filename);
|
||||
if (!file.exists()) {
|
||||
updated = new CardDownloadData(card);
|
||||
updated.setName(card.getName() + " 2");
|
||||
filename = generateImagePath(updated);
|
||||
filename = buildImagePathToCard(updated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,102 +122,127 @@ public final class CardImageUtils {
|
|||
return set;
|
||||
}
|
||||
|
||||
private static String getImageDir(CardDownloadData card, String imagesPath) {
|
||||
public static String prepareCardNameForFile(String cardName){
|
||||
return cardName.replace(":", "").replace("\"", "").replace("//", "-");
|
||||
}
|
||||
|
||||
public static String getImagesDir(){
|
||||
// return real images dir (path without separator)
|
||||
|
||||
String path = null;
|
||||
|
||||
// user path
|
||||
if (!PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true").equals("true")){
|
||||
path = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||
}
|
||||
|
||||
// default path
|
||||
if (path == null) {
|
||||
path = Constants.IO.DEFAULT_IMAGES_DIR;
|
||||
}
|
||||
|
||||
while(path.endsWith(File.separator))
|
||||
{
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String buildImagePathToTokens() {
|
||||
String imagesPath = getImagesDir() + File.separator;
|
||||
|
||||
|
||||
if (PreferencesDialog.isSaveImagesToZip()) {
|
||||
return imagesPath + "TOK.zip" + TFile.separator;
|
||||
} else {
|
||||
return imagesPath + "TOK" + TFile.separator;
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildImagePathToTokenDescriptor(CardDownloadData card) {
|
||||
return buildImagePathToTokens() + card.getTokenDescriptor() + ".full.jpg";
|
||||
}
|
||||
|
||||
public static String buildImagePathToSet(CardDownloadData card) {
|
||||
|
||||
if (card.getSet() == null) {
|
||||
return "";
|
||||
throw new IllegalArgumentException("Card " + card.getName() + " have empty set.");
|
||||
}
|
||||
String set = updateSet(card.getSet(), false).toUpperCase();
|
||||
String imagesDir = (imagesPath != null ? imagesPath : Constants.IO.imageBaseDir);
|
||||
|
||||
String set = updateSet(card.getSet(), false).toUpperCase(); // TODO: research auto-replace... old code?
|
||||
|
||||
if (card.isToken()) {
|
||||
return buildTokenPath(imagesDir, set);
|
||||
return buildImagePathToSetAsToken(set);
|
||||
} else {
|
||||
return buildPath(imagesDir, set);
|
||||
return buildImagePathToSetAsCard(set);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getImageBasePath() {
|
||||
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||
String imagesPath = Objects.equals(useDefault, "true") ? Constants.IO.imageBaseDir : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||
private static String buildImagePathToSetAsCard(String set) {
|
||||
String imagesPath = getImagesDir() + File.separator;
|
||||
|
||||
if (imagesPath != null && !imagesPath.endsWith(TFile.separator)) {
|
||||
imagesPath += TFile.separator;
|
||||
}
|
||||
return imagesPath;
|
||||
}
|
||||
|
||||
public static String getTokenBasePath() {
|
||||
String imagesPath = getImageBasePath();
|
||||
|
||||
String finalPath = "";
|
||||
if (PreferencesDialog.isSaveImagesToZip()) {
|
||||
finalPath = imagesPath + "TOK" + ".zip" + TFile.separator;
|
||||
return imagesPath + set + ".zip" + File.separator;
|
||||
} else {
|
||||
finalPath = imagesPath + "TOK" + TFile.separator;
|
||||
}
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
private static String getTokenDescriptorImagePath(CardDownloadData card) {
|
||||
return getTokenBasePath() + card.getTokenDescriptor() + ".full.jpg";
|
||||
}
|
||||
|
||||
private static String buildTokenPath(String imagesDir, String set) {
|
||||
if (PreferencesDialog.isSaveImagesToZip()) {
|
||||
return imagesDir + TFile.separator + "TOK" + ".zip" + TFile.separator + set;
|
||||
} else {
|
||||
return imagesDir + TFile.separator + "TOK" + TFile.separator + set;
|
||||
return imagesPath + set + File.separator;
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildPath(String imagesDir, String set) {
|
||||
if (PreferencesDialog.isSaveImagesToZip()) {
|
||||
return imagesDir + TFile.separator + set + ".zip" + TFile.separator + set;
|
||||
} else {
|
||||
return imagesDir + TFile.separator + set;
|
||||
}
|
||||
private static String buildImagePathToSetAsToken(String set) {
|
||||
return buildImagePathToTokens() + set + File.separator;
|
||||
}
|
||||
|
||||
public static String generateImagePath(CardDownloadData card) {
|
||||
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||
String imagesPath = Objects.equals(useDefault, "true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||
public static String buildImagePathToCard(CardDownloadData card) {
|
||||
|
||||
String imageDir = getImageDir(card, imagesPath);
|
||||
String imageName;
|
||||
String setPath = buildImagePathToSet(card);
|
||||
|
||||
String type = card.getType() != 0 ? ' ' + Integer.toString(card.getType()) : "";
|
||||
String name = card.getFileName().isEmpty() ? card.getName().replace(":", "").replace("\"", "").replace("//", "-") : card.getFileName();
|
||||
|
||||
if (card.getUsesVariousArt()) {
|
||||
imageName = name + '.' + card.getCollectorId() + ".full.jpg";
|
||||
} else {
|
||||
imageName = name + type + ".full.jpg";
|
||||
String prefixType = "";
|
||||
if(card.getType() != 0){
|
||||
prefixType = " " + Integer.toString(card.getType());
|
||||
}
|
||||
|
||||
if (new TFile(imageDir).exists() && !new TFile(imageDir + TFile.separator + imageName).exists()) {
|
||||
for (String fileName : new TFile(imageDir).list()) {
|
||||
if (fileName.toLowerCase().equals(imageName.toLowerCase())) {
|
||||
imageName = fileName;
|
||||
break;
|
||||
String cardName = card.getFileName();
|
||||
if (cardName.isEmpty()){
|
||||
cardName = prepareCardNameForFile(card.getName());
|
||||
}
|
||||
|
||||
String finalFileName = "";
|
||||
if (card.getUsesVariousArt()){
|
||||
finalFileName = cardName + '.' + card.getCollectorId() + ".full.jpg";
|
||||
}else{
|
||||
finalFileName = cardName + prefixType + ".full.jpg";
|
||||
}
|
||||
|
||||
// if image file exists, correct name (for case sensitive systems)
|
||||
// use TFile for zips
|
||||
TFile dirFile = new TFile(setPath);
|
||||
TFile imageFile = new TFile(setPath + finalFileName);
|
||||
// warning, zip files can be broken
|
||||
try{
|
||||
if (dirFile.exists() && !imageFile.exists()) {
|
||||
// search like names
|
||||
for (String fileName: dirFile.list()) {
|
||||
if (fileName.toLowerCase().equals(finalFileName.toLowerCase())) {
|
||||
finalFileName = fileName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception ex) {
|
||||
log.error("Can't read card name from file, may be it broken: " + setPath);
|
||||
}
|
||||
|
||||
return imageDir + TFile.separator + imageName;
|
||||
return setPath + finalFileName;
|
||||
}
|
||||
|
||||
public static String generateFaceImagePath(String cardname, String set) {
|
||||
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||
String imagesPath = Objects.equals(useDefault, "true") ? Constants.IO.imageBaseDir : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||
String imageDir = imagesPath;
|
||||
String imageName = set + TFile.separator + cardname + ".jpg";
|
||||
return imageDir + TFile.separator + "FACE" + TFile.separator + imageName;
|
||||
return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + File.separator + ".jpg";
|
||||
}
|
||||
|
||||
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
|
||||
// String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||
// String imagesPath = Objects.equals(useDefault, "true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||
|
||||
String straightImageFile = getTokenDescriptorImagePath(card);
|
||||
String straightImageFile = buildImagePathToTokenDescriptor(card);
|
||||
TFile file = new TFile(straightImageFile);
|
||||
if (file.exists()) {
|
||||
return straightImageFile;
|
||||
|
|
|
@ -32,10 +32,9 @@ public interface CardPlugin extends Plugin {
|
|||
/**
|
||||
* Download various symbols (mana, tap, set).
|
||||
*
|
||||
* @param imagesPath Path to check in and store symbols to. Can be null, in
|
||||
* such case default path should be used.
|
||||
* @param imagesDir Path to check in and store symbols to. Can't be null.
|
||||
*/
|
||||
void downloadSymbols(String imagesPath);
|
||||
void downloadSymbols(String imagesDir);
|
||||
|
||||
void onAddCard(MagePermanent card, int count);
|
||||
|
||||
|
|
|
@ -2261,6 +2261,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else if (opponents.size() == 1) {
|
||||
randomOpponentId = game.getOpponents(abilityControllerId).iterator().next();
|
||||
}
|
||||
return randomOpponentId;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
|
@ -46,6 +48,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterAttackingCreature;
|
||||
import mage.filter.common.FilterBlockingCreature;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.events.GameEvent;
|
||||
|
@ -126,36 +129,65 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
|
|||
|
||||
// Choose new creature to block
|
||||
if (permanent.isCreature()) {
|
||||
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, new FilterAttackingCreature(), true);
|
||||
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
|
||||
controller.chooseTarget(outcome, target, source, game);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
||||
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) {
|
||||
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
|
||||
if (chosenGroup != null) {
|
||||
// Relevant ruling for Balduvian Warlord:
|
||||
// 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,”
|
||||
// it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point.
|
||||
|
||||
boolean notYetBlocked = true;
|
||||
if (!chosenGroup.getBlockers().isEmpty()) {
|
||||
notYetBlocked = false;
|
||||
// according to the following mail response from MTG Rules Management about False Orders:
|
||||
// "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C"
|
||||
// therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know)
|
||||
|
||||
List<Permanent> list = new ArrayList<>();
|
||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||
if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) {
|
||||
for (UUID attackingCreatureId : combatGroup.getAttackers()) {
|
||||
Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId);
|
||||
list.add(targetsControllerAttacker);
|
||||
}
|
||||
chosenGroup.addBlocker(permanent.getId(), controller.getId(), game);
|
||||
if (notYetBlocked) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
Player targetsController = game.getPlayer(permanent.getControllerId());
|
||||
if (targetsController != null) {
|
||||
FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName());
|
||||
filter.add(new PermanentInListPredicate(list));
|
||||
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true);
|
||||
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
|
||||
controller.chooseTarget(outcome, target, source, game);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
||||
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) {
|
||||
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
|
||||
if (chosenGroup != null) {
|
||||
// Relevant ruling for Balduvian Warlord:
|
||||
// 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,”
|
||||
// it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point.
|
||||
|
||||
boolean notYetBlocked = chosenGroup.getBlockers().isEmpty();
|
||||
chosenGroup.addBlocker(permanent.getId(), controller.getId(), game);
|
||||
if (notYetBlocked) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class PermanentInListPredicate implements Predicate<Permanent> {
|
||||
|
||||
private final List<Permanent> permanents;
|
||||
|
||||
public PermanentInListPredicate(List<Permanent> permanents) {
|
||||
this.permanents = permanents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return permanents.contains(input);
|
||||
}
|
||||
}
|
||||
|
|
75
Mage.Sets/src/mage/cards/c/CharmPeddler.java
Normal file
75
Mage.Sets/src/mage/cards/c/CharmPeddler.java
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class CharmPeddler extends CardImpl {
|
||||
|
||||
public CharmPeddler(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.SPELLSHAPER);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// {W}, {T}, Discard a card: The next time a source of your choice would deal damage to target creature this turn, prevent that damage.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventNextDamageFromChosenSourceToTargetEffect(Duration.EndOfTurn), new ManaCostsImpl("{W}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new DiscardCardCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public CharmPeddler(final CharmPeddler card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharmPeddler copy() {
|
||||
return new CharmPeddler(this);
|
||||
}
|
||||
}
|
152
Mage.Sets/src/mage/cards/d/DarkSphere.java
Normal file
152
Mage.Sets/src/mage/cards/d/DarkSphere.java
Normal 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
|
||||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterObject;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamageEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetSource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ThomasLerner
|
||||
*/
|
||||
public class DarkSphere extends CardImpl {
|
||||
|
||||
public DarkSphere(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}");
|
||||
|
||||
|
||||
// {tap}, Sacrifice Dark Sphere: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DarkSpherePreventionEffect(), new TapSourceCost());
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public DarkSphere(final DarkSphere card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DarkSphere copy() {
|
||||
return new DarkSphere(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DarkSpherePreventionEffect extends ReplacementEffectImpl {
|
||||
|
||||
private final TargetSource targetSource;
|
||||
|
||||
public DarkSpherePreventionEffect() {
|
||||
super(Duration.OneUse, Outcome.RedirectDamage);
|
||||
this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down";
|
||||
this.targetSource = new TargetSource(new FilterObject("source of your choice"));
|
||||
}
|
||||
|
||||
public DarkSpherePreventionEffect(final DarkSpherePreventionEffect effect) {
|
||||
super(effect);
|
||||
this.targetSource = effect.targetSource.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DarkSpherePreventionEffect copy() {
|
||||
return new DarkSpherePreventionEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
DamageEvent damageEvent = (DamageEvent) event;
|
||||
if (controller != null) {
|
||||
controller.damage((int) Math.ceil(damageEvent.getAmount() / 2.0), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects());
|
||||
StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : "");
|
||||
sb.append(": ").append(damageEvent.getAmount() / 2).append(" damage prevented");
|
||||
sb.append(" from ").append(controller.getLogName());
|
||||
game.informPlayers(sb.toString());
|
||||
discard(); // only one use
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
58
Mage.Sets/src/mage/cards/d/DefensiveFormation.java
Normal file
58
Mage.Sets/src/mage/cards/d/DefensiveFormation.java
Normal 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.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class DefensiveFormation extends CardImpl {
|
||||
|
||||
public DefensiveFormation(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}");
|
||||
|
||||
// Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it.
|
||||
this.addAbility(ControllerAssignCombatDamageToBlockersAbility.getInstance());
|
||||
}
|
||||
|
||||
public DefensiveFormation(final DefensiveFormation card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefensiveFormation copy() {
|
||||
return new DefensiveFormation(this);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@
|
|||
*/
|
||||
package mage.cards.f;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -43,6 +45,7 @@ import mage.filter.FilterPermanent;
|
|||
import mage.filter.common.FilterAttackingCreature;
|
||||
import mage.filter.predicate.ObjectPlayer;
|
||||
import mage.filter.predicate.ObjectPlayerPredicate;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
|
@ -140,37 +143,66 @@ class FalseOrdersUnblockEffect extends OneShotEffect {
|
|||
// Choose new creature to block
|
||||
if (permanent.isCreature()) {
|
||||
if (controller.chooseUse(Outcome.Benefit, "Do you want " + permanent.getLogName() + " to block an attacking creature?", source, game)) {
|
||||
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, new FilterAttackingCreature(), true);
|
||||
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
|
||||
controller.chooseTarget(outcome, target, source, game);
|
||||
// according to the following mail response from MTG Rules Management about False Orders:
|
||||
// "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C"
|
||||
// therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know)
|
||||
|
||||
List<Permanent> list = new ArrayList<>();
|
||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||
if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) {
|
||||
for (UUID attackingCreatureId : combatGroup.getAttackers()) {
|
||||
Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId);
|
||||
list.add(targetsControllerAttacker);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
||||
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) {
|
||||
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
|
||||
if (chosenGroup != null) {
|
||||
// Relevant ruling for Balduvian Warlord:
|
||||
// 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,”
|
||||
// it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point.
|
||||
|
||||
boolean notYetBlocked = true;
|
||||
if (!chosenGroup.getBlockers().isEmpty()) {
|
||||
notYetBlocked = false;
|
||||
Player targetsController = game.getPlayer(permanent.getControllerId());
|
||||
if (targetsController != null) {
|
||||
FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName());
|
||||
filter.add(new PermanentInListPredicate(list));
|
||||
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true);
|
||||
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
|
||||
controller.chooseTarget(outcome, target, source, game);
|
||||
}
|
||||
chosenGroup.addBlocker(permanent.getId(), controller.getId(), game);
|
||||
if (notYetBlocked) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
||||
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) {
|
||||
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
|
||||
if (chosenGroup != null) {
|
||||
// Relevant ruling for Balduvian Warlord:
|
||||
// 7/15/2006 If an attacking creature has an ability that triggers “When this creature becomes blocked,”
|
||||
// it triggers when a creature blocks it due to the Warlord’s ability only if it was unblocked at that point.
|
||||
|
||||
boolean notYetBlocked = chosenGroup.getBlockers().isEmpty();
|
||||
chosenGroup.addBlocker(permanent.getId(), controller.getId(), game);
|
||||
if (notYetBlocked) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class PermanentInListPredicate implements Predicate<Permanent> {
|
||||
|
||||
private final List<Permanent> permanents;
|
||||
|
||||
public PermanentInListPredicate(List<Permanent> permanents) {
|
||||
this.permanents = permanents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return permanents.contains(input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
|
@ -40,12 +39,10 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.Watcher;
|
||||
import mage.watchers.common.AttackedLastTurnWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -61,7 +58,7 @@ public class GiantTurtle extends CardImpl {
|
|||
this.toughness = new MageInt(4);
|
||||
|
||||
// Giant Turtle can't attack if it attacked during your last turn.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GiantTurtleCantAttackEffect()), new GiantTurtleWatcher());
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnEffect()), new AttackedLastTurnWatcher());
|
||||
}
|
||||
|
||||
public GiantTurtle(final GiantTurtle card) {
|
||||
|
@ -74,14 +71,14 @@ public class GiantTurtle extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class GiantTurtleCantAttackEffect extends RestrictionEffect {
|
||||
class CantAttackIfAttackedLastTurnEffect extends RestrictionEffect {
|
||||
|
||||
public GiantTurtleCantAttackEffect() {
|
||||
public CantAttackIfAttackedLastTurnEffect() {
|
||||
super(Duration.WhileOnBattlefield);
|
||||
staticText = "{this} can't attack if it attacked during your last turn";
|
||||
}
|
||||
|
||||
public GiantTurtleCantAttackEffect(final GiantTurtleCantAttackEffect effect) {
|
||||
public CantAttackIfAttackedLastTurnEffect(final CantAttackIfAttackedLastTurnEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -92,51 +89,20 @@ class GiantTurtleCantAttackEffect extends RestrictionEffect {
|
|||
|
||||
@Override
|
||||
public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) {
|
||||
GiantTurtleWatcher watcher = (GiantTurtleWatcher) game.getState().getWatchers().get(GiantTurtleWatcher.class.getSimpleName());
|
||||
for (MageObjectReference mor : watcher.getAttackedLastTurnCreatures()) {
|
||||
if (attacker.equals(mor.getPermanent(game)) && attacker.getZoneChangeCounter(game) == mor.getZoneChangeCounter()) {
|
||||
AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<MageObjectReference> attackingCreatures = watcher.getAttackedLastTurnCreatures(attacker.getControllerId());
|
||||
MageObjectReference mor = new MageObjectReference(attacker, game);
|
||||
if (attackingCreatures.contains(mor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GiantTurtleCantAttackEffect copy() {
|
||||
return new GiantTurtleCantAttackEffect(this);
|
||||
public CantAttackIfAttackedLastTurnEffect copy() {
|
||||
return new CantAttackIfAttackedLastTurnEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GiantTurtleWatcher extends Watcher {
|
||||
|
||||
public final Set<MageObjectReference> attackedLastTurnCreatures = new HashSet<>();
|
||||
|
||||
public GiantTurtleWatcher() {
|
||||
super(GiantTurtleWatcher.class.getSimpleName(), WatcherScope.GAME);
|
||||
}
|
||||
|
||||
public GiantTurtleWatcher(final GiantTurtleWatcher watcher) {
|
||||
super(watcher);
|
||||
this.attackedLastTurnCreatures.addAll(watcher.attackedLastTurnCreatures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
|
||||
this.attackedLastTurnCreatures.add(new MageObjectReference(event.getSourceId(), game));
|
||||
}
|
||||
if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) {
|
||||
this.attackedLastTurnCreatures.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<MageObjectReference> getAttackedLastTurnCreatures() {
|
||||
return this.attackedLastTurnCreatures;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GiantTurtleWatcher copy() {
|
||||
return new GiantTurtleWatcher(this);
|
||||
}
|
||||
}
|
||||
|
|
75
Mage.Sets/src/mage/cards/g/GlyphOfDestruction.java
Normal file
75
Mage.Sets/src/mage/cards/g/GlyphOfDestruction.java
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.DestroyTargetAtBeginningOfNextEndStepEffect;
|
||||
import mage.abilities.effects.common.PreventDamageToTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.BlockingPredicate;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class GlyphOfDestruction extends CardImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("blocking Wall you control");
|
||||
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.WALL));
|
||||
filter.add(new BlockingPredicate());
|
||||
}
|
||||
|
||||
public GlyphOfDestruction(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}");
|
||||
|
||||
// Target blocking Wall you control gets +10/+0 until end of combat. Prevent all damage that would be dealt to it this turn. Destroy it at the beginning of the next end step.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(10, 0, Duration.EndOfCombat));
|
||||
this.getSpellAbility().addEffect(new PreventDamageToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE).setText("Prevent all damage that would be dealt to it this turn"));
|
||||
this.getSpellAbility().addEffect(new DestroyTargetAtBeginningOfNextEndStepEffect().setText("Destroy it at the beginning of the next end step"));
|
||||
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(filter));
|
||||
}
|
||||
|
||||
public GlyphOfDestruction(final GlyphOfDestruction card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlyphOfDestruction copy() {
|
||||
return new GlyphOfDestruction(this);
|
||||
}
|
||||
}
|
107
Mage.Sets/src/mage/cards/h/HallsOfMist.java
Normal file
107
Mage.Sets/src/mage/cards/h/HallsOfMist.java
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.abilities.keyword.CumulativeUpkeepAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.AttackedLastTurnWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class HallsOfMist extends CardImpl {
|
||||
|
||||
public HallsOfMist(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||
|
||||
// Cumulative upkeep-Pay {1}.
|
||||
this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(1)));
|
||||
|
||||
// Creatures that attacked during their controller's last turn can't attack.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackIfAttackedLastTurnAllEffect()), new AttackedLastTurnWatcher());
|
||||
}
|
||||
|
||||
public HallsOfMist(final HallsOfMist card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HallsOfMist copy() {
|
||||
return new HallsOfMist(this);
|
||||
}
|
||||
}
|
||||
|
||||
class CantAttackIfAttackedLastTurnAllEffect extends RestrictionEffect {
|
||||
|
||||
public CantAttackIfAttackedLastTurnAllEffect() {
|
||||
super(Duration.WhileOnBattlefield);
|
||||
this.staticText = "Creatures that attacked during their controller's last turn can't attack";
|
||||
}
|
||||
|
||||
public CantAttackIfAttackedLastTurnAllEffect(final CantAttackIfAttackedLastTurnAllEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
return permanent.isCreature();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game) {
|
||||
AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<MageObjectReference> attackingCreatures = watcher.getAttackedLastTurnCreatures(attacker.getControllerId());
|
||||
MageObjectReference mor = new MageObjectReference(attacker, game);
|
||||
if (attackingCreatures.contains(mor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantAttackIfAttackedLastTurnAllEffect copy() {
|
||||
return new CantAttackIfAttackedLastTurnAllEffect(this);
|
||||
}
|
||||
|
||||
}
|
118
Mage.Sets/src/mage/cards/h/HiredGiant.java
Normal file
118
Mage.Sets/src/mage/cards/h/HiredGiant.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterLandCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000 & L_J
|
||||
*/
|
||||
public class HiredGiant extends CardImpl {
|
||||
|
||||
public HiredGiant(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}");
|
||||
this.subtype.add(SubType.GIANT);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// When Hired Giant enters the battlefield, each other player may search his or her library for a land card and put that card onto the battlefield. Then each player who searched his or her library this way shuffles it.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new HiredGiantEffect()));
|
||||
}
|
||||
|
||||
public HiredGiant(final HiredGiant card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HiredGiant copy() {
|
||||
return new HiredGiant(this);
|
||||
}
|
||||
}
|
||||
|
||||
class HiredGiantEffect extends OneShotEffect {
|
||||
|
||||
HiredGiantEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "each other player may search his or her library for a land card and put that card onto the battlefield. Then each player who searched his or her library this way shuffles it";
|
||||
}
|
||||
|
||||
HiredGiantEffect(final HiredGiantEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HiredGiantEffect copy() {
|
||||
return new HiredGiantEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Set<Player> playersThatSearched = new HashSet<>(1);
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
if (playerId != controller.getId()) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null && player.chooseUse(Outcome.PutCreatureInPlay, "Search your library for a land card and put it onto the battlefield?", source, game)) {
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(new FilterLandCard());
|
||||
if (player.searchLibrary(target, game)) {
|
||||
Card targetCard = player.getLibrary().getCard(target.getFirstTarget(), game);
|
||||
if (targetCard != null) {
|
||||
player.moveCards(targetCard, Zone.BATTLEFIELD, source, game);
|
||||
playersThatSearched.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Player player : playersThatSearched) {
|
||||
player.shuffleLibrary(source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
191
Mage.Sets/src/mage/cards/i/Imprison.java
Normal file
191
Mage.Sets/src/mage/cards/i/Imprison.java
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.i;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.AttacksOrBlocksEnchantedTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.CounterTargetEffect;
|
||||
import mage.abilities.effects.common.DestroySourceEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.RemoveFromCombatTargetEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class Imprison extends CardImpl {
|
||||
|
||||
public Imprison(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}");
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
// Enchant creature
|
||||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
|
||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever a player activates an ability of enchanted creature with {T} in its activation cost that isn't a mana ability, you may pay {1}. If you do, counter that ability. If you don't, destroy Imprison.
|
||||
this.addAbility(new ImprisonTriggeredAbility());
|
||||
|
||||
// Whenever enchanted creature attacks or blocks, you may pay {1}. If you do, tap the creature, remove it from combat, and creatures it was blocking that had become blocked by only that creature this combat become unblocked. If you don't, destroy Imprison.
|
||||
this.addAbility(new AttacksOrBlocksEnchantedTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new ImprisonUnblockEffect(), new DestroySourceEffect(), new ManaCostsImpl("1"))));
|
||||
}
|
||||
|
||||
public Imprison(final Imprison card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Imprison copy() {
|
||||
return new Imprison(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ImprisonTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
ImprisonTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DoIfCostPaid(new CounterTargetEffect().setText("counter that ability"), new DestroySourceEffect(), new ManaCostsImpl("1")));
|
||||
}
|
||||
|
||||
ImprisonTriggeredAbility(final ImprisonTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImprisonTriggeredAbility copy() {
|
||||
return new ImprisonTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.ACTIVATED_ABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent enchantment = game.getPermanentOrLKIBattlefield(sourceId);
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null) {
|
||||
Permanent enchantedPermanent = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo());
|
||||
if (event.getSourceId().equals(enchantedPermanent.getId())) {
|
||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
|
||||
if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) {
|
||||
String abilityText = stackAbility.getRule(true);
|
||||
if (abilityText.contains("{T},") || abilityText.contains("{T}:") || abilityText.contains("{T} or")) {
|
||||
getEffects().get(0).setTargetPointer(new FixedTarget(stackAbility.getId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever a player activates an ability of enchanted creature with {T} in its activation cost that isn't a mana ability, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
||||
class ImprisonUnblockEffect extends OneShotEffect {
|
||||
|
||||
public ImprisonUnblockEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "tap the creature, remove it from combat, and creatures it was blocking that had become blocked by only that creature this combat become unblocked";
|
||||
}
|
||||
|
||||
public ImprisonUnblockEffect(final ImprisonUnblockEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImprisonUnblockEffect copy() {
|
||||
return new ImprisonUnblockEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null) {
|
||||
Permanent permanent = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (permanent != null) {
|
||||
if (permanent.isCreature()) {
|
||||
|
||||
// Tap the creature
|
||||
permanent.tap(game);
|
||||
|
||||
// Remove it from combat
|
||||
Effect effect = new RemoveFromCombatTargetEffect();
|
||||
effect.setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
effect.apply(game, source);
|
||||
|
||||
// Make blocked creatures unblocked
|
||||
BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<CombatGroup> combatGroups = watcher.getBlockedOnlyByCreature(permanent.getId());
|
||||
if (combatGroups != null) {
|
||||
for (CombatGroup combatGroup : combatGroups) {
|
||||
if (combatGroup != null) {
|
||||
combatGroup.setBlocked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
73
Mage.Sets/src/mage/cards/i/Inviolability.java
Normal file
73
Mage.Sets/src/mage/cards/i/Inviolability.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.i;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.PreventAllDamageToAttachedEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class Inviolability extends CardImpl {
|
||||
|
||||
public Inviolability(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}");
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
// Enchant creature
|
||||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit));
|
||||
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
|
||||
|
||||
// Prevent all damage that would be dealt to enchanted creature.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventAllDamageToAttachedEffect(Duration.WhileOnBattlefield, "enchanted creature", false)));
|
||||
}
|
||||
|
||||
public Inviolability(final Inviolability card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inviolability copy() {
|
||||
return new Inviolability(this);
|
||||
}
|
||||
}
|
210
Mage.Sets/src/mage/cards/r/RagingRiver.java
Normal file
210
Mage.Sets/src/mage/cards/r/RagingRiver.java
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class RagingRiver extends CardImpl {
|
||||
|
||||
public RagingRiver(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{R}");
|
||||
|
||||
// Whenever one or more creatures you control attack, each defending player divides all creatures without flying he or she controls into a "left" pile and a "right" pile. Then, for each attacking creature you control, choose "left" or "right." That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label.
|
||||
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new RagingRiverEffect(), 1));
|
||||
}
|
||||
|
||||
public RagingRiver(final RagingRiver card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RagingRiver copy() {
|
||||
return new RagingRiver(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RagingRiverEffect extends OneShotEffect {
|
||||
|
||||
public RagingRiverEffect() {
|
||||
super(Outcome.Detriment);
|
||||
staticText = "each defending player divides all creatures without flying he or she controls into a \"left\" pile and a \"right\" pile. Then, for each attacking creature you control, choose \"left\" or \"right.\" That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label";
|
||||
}
|
||||
|
||||
public RagingRiverEffect(final RagingRiverEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RagingRiverEffect copy() {
|
||||
return new RagingRiverEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
List<Permanent> left = new ArrayList<>();
|
||||
List<Permanent> right = new ArrayList<>();
|
||||
|
||||
for (UUID defenderId : game.getCombat().getPlayerDefenders(game)) {
|
||||
Player defender = game.getPlayer(defenderId);
|
||||
if (defender != null) {
|
||||
List<Permanent> leftLog = new ArrayList<>();
|
||||
List<Permanent> rightLog = new ArrayList<>();
|
||||
FilterControlledCreaturePermanent filterBlockers = new FilterControlledCreaturePermanent("creatures without flying you control to assign to the \"left\" pile (creatures not chosen will be assigned to the \"right\" pile)");
|
||||
filterBlockers.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
|
||||
Target target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filterBlockers, true);
|
||||
if (target.canChoose(source.getSourceId(), defenderId, game)) {
|
||||
if (defender.chooseTarget(Outcome.Neutral, target, source, game)) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), defenderId, game)) {
|
||||
if (target.getTargets().contains(permanent.getId())) {
|
||||
left.add(permanent);
|
||||
leftLog.add(permanent);
|
||||
}
|
||||
else if (filterBlockers.match(permanent, source.getSourceId(), defenderId, game)) {
|
||||
right.add(permanent);
|
||||
rightLog.add(permanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it could be nice to invoke some graphic indicator of which creature is Left or Right in this spot
|
||||
StringBuilder sb = new StringBuilder("Left pile of ").append(defender.getLogName()).append(": ");
|
||||
int i = 0;
|
||||
for (Permanent permanent : leftLog) {
|
||||
i++;
|
||||
sb.append(permanent.getLogName());
|
||||
if (i < leftLog.size()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
game.informPlayers(sb.toString());
|
||||
|
||||
sb = new StringBuilder("Right pile of ").append(defender.getLogName()).append(": ");
|
||||
i = 0;
|
||||
for (Permanent permanent : rightLog) {
|
||||
i++;
|
||||
sb.append(permanent.getLogName());
|
||||
if (i < rightLog.size()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
game.informPlayers(sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID attackers : game.getCombat().getAttackers()) {
|
||||
Permanent attacker = game.getPermanent(attackers);
|
||||
if (attacker != null && attacker.getControllerId() == controller.getId()) {
|
||||
CombatGroup combatGroup = game.getCombat().findGroup(attacker.getId());
|
||||
if (combatGroup != null) {
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
Player defender = game.getPlayer(combatGroup.getDefendingPlayerId());
|
||||
if (defender != null) {
|
||||
if (left.isEmpty() && right.isEmpty()) {
|
||||
// shortcut in case of no labeled blockers available
|
||||
filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
|
||||
} else {
|
||||
List<Permanent> leftLog = new ArrayList<>();
|
||||
List<Permanent> rightLog = new ArrayList<>();
|
||||
|
||||
for (Permanent permanent : left) {
|
||||
if (permanent.getControllerId() == defender.getId()) {
|
||||
leftLog.add(permanent);
|
||||
}
|
||||
}
|
||||
for (Permanent permanent : right) {
|
||||
if (permanent.getControllerId() == defender.getId()) {
|
||||
rightLog.add(permanent);
|
||||
}
|
||||
}
|
||||
|
||||
if (controller.choosePile(outcome, attacker.getName() + ": attacking " + defender.getName(), leftLog, rightLog, game)) {
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(left))));
|
||||
game.informPlayers(attacker.getLogName() + ": attacks left (" + defender.getLogName() + ")");
|
||||
} else {
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(right))));
|
||||
game.informPlayers(attacker.getLogName() + ": attacks right (" + defender.getLogName() + ")");
|
||||
}
|
||||
}
|
||||
RestrictionEffect effect = new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfCombat);
|
||||
effect.setTargetPointer(new FixedTarget(attacker.getId()));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class PermanentInListPredicate implements Predicate<Permanent> {
|
||||
|
||||
private final List<Permanent> permanents;
|
||||
|
||||
public PermanentInListPredicate(List<Permanent> permanents) {
|
||||
this.permanents = permanents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return permanents.contains(input);
|
||||
}
|
||||
}
|
130
Mage.Sets/src/mage/cards/r/RemoveEnchantments.java
Normal file
130
Mage.Sets/src/mage/cards/r/RemoveEnchantments.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.DestroyAllEffect;
|
||||
import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledEnchantmentPermanent;
|
||||
import mage.filter.predicate.ObjectPlayer;
|
||||
import mage.filter.predicate.ObjectPlayerPredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.other.OwnerPredicate;
|
||||
import mage.filter.predicate.permanent.AttachedToControlledPermanentPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class RemoveEnchantments extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter1 = new FilterControlledEnchantmentPermanent();
|
||||
private static final FilterPermanent filter2 = new FilterPermanent();
|
||||
private static final FilterPermanent filter3 = new FilterPermanent();
|
||||
private static final FilterPermanent filter4 = new FilterControlledEnchantmentPermanent();
|
||||
private static final FilterPermanent filter5 = new FilterPermanent();
|
||||
private static final FilterPermanent filter6 = new FilterPermanent();
|
||||
static {
|
||||
// all enchantments you both own and control
|
||||
filter1.add(new OwnerPredicate(TargetController.YOU));
|
||||
// all Auras you own attached to permanents you control
|
||||
filter2.add(new AttachedToControlledPermanentPredicate());
|
||||
filter2.add(new SubtypePredicate(SubType.AURA));
|
||||
filter2.add(new OwnerPredicate(TargetController.YOU));
|
||||
// all Auras you own attached to attacking creatures your opponents control
|
||||
filter3.add(new AttachedToOpponentControlledAttackingCreaturePredicate());
|
||||
filter3.add(new SubtypePredicate(SubType.AURA));
|
||||
filter3.add(new OwnerPredicate(TargetController.YOU));
|
||||
// all other enchantments you control (i.e. that you don't own)
|
||||
filter4.add(new OwnerPredicate(TargetController.NOT_YOU));
|
||||
// all other Auras attached to permanents you control (i.e. that you don't own)
|
||||
filter5.add(new AttachedToControlledPermanentPredicate());
|
||||
filter5.add(new SubtypePredicate(SubType.AURA));
|
||||
filter5.add(new OwnerPredicate(TargetController.NOT_YOU));
|
||||
// all other Auras attached to attacking creatures your opponents control (i.e. that you don't own)
|
||||
filter6.add(new AttachedToOpponentControlledAttackingCreaturePredicate());
|
||||
filter6.add(new SubtypePredicate(SubType.AURA));
|
||||
filter6.add(new OwnerPredicate(TargetController.NOT_YOU));
|
||||
}
|
||||
|
||||
public RemoveEnchantments(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}");
|
||||
|
||||
// Return to your hand all enchantments you both own and control, all Auras you own attached to permanents you control, and all Auras you own attached to attacking creatures your opponents control. Then destroy all other enchantments you control, all other Auras attached to permanents you control, and all other Auras attached to attacking creatures your opponents control.
|
||||
this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter1).setText("Return to your hand all enchantments you both own and control,"));
|
||||
this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter2).setText(" all Auras you own attached to permanents you control"));
|
||||
this.getSpellAbility().addEffect(new ReturnToHandFromBattlefieldAllEffect(filter3).setText("and all Auras you own attached to attacking creatures your opponents control"));
|
||||
this.getSpellAbility().addEffect(new DestroyAllEffect(filter4).setText("Then destroy all other enchantments you control,"));
|
||||
this.getSpellAbility().addEffect(new DestroyAllEffect(filter5).setText(" all other Auras attached to permanents you control"));
|
||||
this.getSpellAbility().addEffect(new DestroyAllEffect(filter6).setText("and all other Auras attached to attacking creatures your opponents control"));
|
||||
}
|
||||
|
||||
public RemoveEnchantments(final RemoveEnchantments card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveEnchantments copy() {
|
||||
return new RemoveEnchantments(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AttachedToOpponentControlledAttackingCreaturePredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent>> {
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectPlayer<Permanent> input, Game game) {
|
||||
Permanent attachement = input.getObject();
|
||||
if (attachement != null) {
|
||||
Permanent permanent = game.getPermanent(attachement.getAttachedTo());
|
||||
if (permanent != null) {
|
||||
if (permanent.isCreature()) {
|
||||
if (permanent.isAttacking()) {
|
||||
if (!permanent.getControllerId().equals(input.getPlayerId()) &&
|
||||
game.getPlayer(input.getPlayerId()).hasOpponent(permanent.getControllerId(), game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Attached to attacking creatures your opponents control";
|
||||
}
|
||||
}
|
102
Mage.Sets/src/mage/cards/r/Renounce.java
Normal file
102
Mage.Sets/src/mage/cards/r/Renounce.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class Renounce extends CardImpl {
|
||||
|
||||
public Renounce(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
|
||||
|
||||
// Sacrifice any number of permanents. You gain 2 life for each permanent sacrificed this way.
|
||||
this.getSpellAbility().addEffect(new RenounceEffect());
|
||||
}
|
||||
|
||||
public Renounce(final Renounce card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renounce copy() {
|
||||
return new Renounce(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RenounceEffect extends OneShotEffect {
|
||||
|
||||
public RenounceEffect() {
|
||||
super(Outcome.Neutral);
|
||||
staticText = "Sacrifice any number of permanents. You gain 2 life for each permanent sacrificed this way";
|
||||
}
|
||||
|
||||
public RenounceEffect(final RenounceEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenounceEffect copy() {
|
||||
return new RenounceEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null){
|
||||
return false;
|
||||
}
|
||||
int amount = 0;
|
||||
TargetControlledPermanent toSacrifice = new TargetControlledPermanent(0, Integer.MAX_VALUE, new FilterControlledPermanent(), true);
|
||||
if(player.chooseTarget(Outcome.Sacrifice, toSacrifice, source, game)) {
|
||||
for(Object uuid : toSacrifice.getTargets()){
|
||||
Permanent permanent = game.getPermanent((UUID)uuid);
|
||||
if(permanent != null){
|
||||
permanent.sacrifice(source.getSourceId(), game);
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
player.gainLife(amount * 2, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
106
Mage.Sets/src/mage/cards/r/RootwaterShaman.java
Normal file
106
Mage.Sets/src/mage/cards/r/RootwaterShaman.java
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class RootwaterShaman extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("Aura spells with enchant creature");
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.AURA));
|
||||
filter.add(new RootwaterShamanAbilityPredicate());
|
||||
}
|
||||
|
||||
public RootwaterShaman(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
|
||||
this.subtype.add(SubType.MERFOLK);
|
||||
this.subtype.add(SubType.SHAMAN);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// You may cast Aura spells with enchant creature as though they had flash.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CastAsThoughItHadFlashAllEffect(Duration.WhileOnBattlefield, filter, false)));
|
||||
}
|
||||
|
||||
public RootwaterShaman(final RootwaterShaman card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootwaterShaman copy() {
|
||||
return new RootwaterShaman(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RootwaterShamanAbilityPredicate implements Predicate<MageObject> {
|
||||
|
||||
public RootwaterShamanAbilityPredicate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
Abilities<Ability> abilities = input.getAbilities();
|
||||
for (int i = 0; i < abilities.size(); i++) {
|
||||
if (abilities.get(i) instanceof EnchantAbility) {
|
||||
String enchantText = abilities.get(i).getRule();
|
||||
if (enchantText.contentEquals("Enchant creature")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Aura card with enchant creature";
|
||||
}
|
||||
}
|
106
Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java
Normal file
106
Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class SaprazzanBreaker extends CardImpl {
|
||||
|
||||
public SaprazzanBreaker(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}");
|
||||
this.subtype.add(SubType.BEAST);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// {U}: Put the top card of your library into your graveyard. If that card is a land card, Saprazzan Breaker can't be blocked this turn.
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SaprazzanBreakerEffect(), new ManaCostsImpl("{U}")));
|
||||
}
|
||||
|
||||
public SaprazzanBreaker(final SaprazzanBreaker card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaprazzanBreaker copy() {
|
||||
return new SaprazzanBreaker(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SaprazzanBreakerEffect extends OneShotEffect {
|
||||
|
||||
public SaprazzanBreakerEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, {this} can't be blocked this turn";
|
||||
}
|
||||
|
||||
public SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaprazzanBreakerEffect copy() {
|
||||
return new SaprazzanBreakerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
Card card = player.getLibrary().removeFromTop(game);
|
||||
if (card != null) {
|
||||
player.moveCards(card, Zone.GRAVEYARD, source, game);
|
||||
if (card.isLand()) {
|
||||
game.addEffect(new CantBeBlockedByAllSourceEffect(new FilterCreaturePermanent(), Duration.EndOfCombat), source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
67
Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java
Normal file
67
Mage.Sets/src/mage/cards/s/SaprazzanOutrigger.java
Normal 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.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.PutOnLibrarySourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class SaprazzanOutrigger extends CardImpl {
|
||||
|
||||
public SaprazzanOutrigger(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}");
|
||||
this.subtype.add(SubType.MERFOLK);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(5);
|
||||
|
||||
// When Saprazzan Outrigger attacks or blocks, put it on top of its owner's library at end of combat.
|
||||
DelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(new PutOnLibrarySourceEffect(true, "put it on top of its owner's library at end of combat"));
|
||||
this.addAbility(new AttacksOrBlocksTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(ability, true), false));
|
||||
}
|
||||
|
||||
public SaprazzanOutrigger(final SaprazzanOutrigger card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaprazzanOutrigger copy() {
|
||||
return new SaprazzanOutrigger(this);
|
||||
}
|
||||
}
|
114
Mage.Sets/src/mage/cards/s/Scarecrow.java
Normal file
114
Mage.Sets/src/mage/cards/s/Scarecrow.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagePlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class Scarecrow extends CardImpl {
|
||||
|
||||
public Scarecrow(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}");
|
||||
this.subtype.add(SubType.SCARECROW);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// {6}, {T}: Prevent all damage that would be dealt to you this turn by attacking creatures without flying.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScarecrowEffect(), new GenericManaCost(6));
|
||||
ability.addCost(new TapSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
||||
public Scarecrow(final Scarecrow card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scarecrow copy() {
|
||||
return new Scarecrow(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ScarecrowEffect extends PreventionEffectImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
static {
|
||||
filter.add(new AbilityPredicate(FlyingAbility.class));
|
||||
}
|
||||
|
||||
ScarecrowEffect() {
|
||||
super(Duration.EndOfTurn, Integer.MAX_VALUE, false);
|
||||
staticText = "Prevent all damage that would be dealt to you this turn by creatures with flying";
|
||||
}
|
||||
|
||||
ScarecrowEffect(final ScarecrowEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScarecrowEffect copy() {
|
||||
return new ScarecrowEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) {
|
||||
DamagePlayerEvent damageEvent = (DamagePlayerEvent) event;
|
||||
if (event.getTargetId().equals(source.getControllerId())) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId());
|
||||
if (permanent != null && filter.match(permanent, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ import mage.target.common.TargetCardInLibrary;
|
|||
*/
|
||||
public class Tallowisp extends CardImpl {
|
||||
|
||||
private static final FilterCard filterAura = new FilterCard("Aura card");
|
||||
private static final FilterCard filterAura = new FilterCard("Aura card with enchant creature");
|
||||
|
||||
static {
|
||||
filterAura.add(new CardTypePredicate(CardType.ENCHANTMENT));
|
||||
|
@ -93,7 +93,7 @@ class TallowispAbilityPredicate implements Predicate<MageObject> {
|
|||
for (int i = 0; i < abilities.size(); i++) {
|
||||
if (abilities.get(i) instanceof EnchantAbility) {
|
||||
String enchantText = abilities.get(i).getRule();
|
||||
if (enchantText.startsWith("Enchant") && enchantText.contains("creature")) {
|
||||
if (enchantText.contentEquals("Enchant creature")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
73
Mage.Sets/src/mage/cards/t/TowerOfCoireall.java
Normal file
73
Mage.Sets/src/mage/cards/t/TowerOfCoireall.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class TowerOfCoireall extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Walls");
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.WALL));
|
||||
}
|
||||
|
||||
public TowerOfCoireall(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
|
||||
|
||||
// {tap}: Target creature can't be blocked by Walls this turn.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedByAllTargetEffect(filter, Duration.EndOfTurn).setText("Target creature can't be blocked by Walls this turn"), new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public TowerOfCoireall(final TowerOfCoireall card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TowerOfCoireall copy() {
|
||||
return new TowerOfCoireall(this);
|
||||
}
|
||||
}
|
124
Mage.Sets/src/mage/cards/t/TrapRunner.java
Normal file
124
Mage.Sets/src/mage/cards/t/TrapRunner.java
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.CompoundCondition;
|
||||
import mage.abilities.condition.common.AfterBlockersAreDeclaredCondition;
|
||||
import mage.abilities.condition.common.IsPhaseCondition;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.decorator.ConditionalActivatedAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TurnPhase;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
import mage.filter.predicate.permanent.BlockedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class TrapRunner extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("unblocked attacking creature");
|
||||
|
||||
static {
|
||||
filter.add(new AttackingPredicate());
|
||||
filter.add(Predicates.not(new BlockedPredicate()));
|
||||
}
|
||||
|
||||
public TrapRunner(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.SOLDIER);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// {T}: Target unblocked attacking creature becomes blocked. Activate this ability only during combat after blockers are declared.
|
||||
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new TrapRunnerEffect(), new TapSourceCost(),
|
||||
new CompoundCondition("during combat after blockers are declared", new IsPhaseCondition(TurnPhase.COMBAT), AfterBlockersAreDeclaredCondition.instance));
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public TrapRunner(final TrapRunner card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrapRunner copy() {
|
||||
return new TrapRunner(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TrapRunnerEffect extends OneShotEffect {
|
||||
|
||||
public TrapRunnerEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Target unblocked attacking creature becomes blocked";
|
||||
}
|
||||
|
||||
public TrapRunnerEffect(final TrapRunnerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrapRunnerEffect copy() {
|
||||
return new TrapRunnerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (controller != null && permanent != null) {
|
||||
CombatGroup combatGroup = game.getCombat().findGroup(permanent.getId());
|
||||
if (combatGroup != null) {
|
||||
combatGroup.setBlocked(true);
|
||||
game.informPlayers(permanent.getLogName() + " has become blocked");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
package mage.cards.y;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -38,13 +39,14 @@ import mage.constants.CardType;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import mage.watchers.common.BlockedByOnlyOneCreatureThisCombatWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author MarcoMarin
|
||||
* @author MarcoMarin & L_J
|
||||
*/
|
||||
public class YdwenEfreet extends CardImpl {
|
||||
|
||||
|
@ -72,7 +74,7 @@ class YdwenEfreetEffect extends OneShotEffect {
|
|||
|
||||
public YdwenEfreetEffect() {
|
||||
super(Outcome.Damage);
|
||||
staticText = "flip a coin. If you lose the flip, remove {this} from combat and it can't block.";
|
||||
staticText = "flip a coin. If you lose the flip, remove {this} from combat and it can't block. Creatures it was blocking that had become blocked by only {this} this combat become unblocked";
|
||||
}
|
||||
|
||||
public YdwenEfreetEffect(YdwenEfreetEffect effect) {
|
||||
|
@ -84,14 +86,25 @@ class YdwenEfreetEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent creature = game.getPermanent(source.getSourceId());
|
||||
if (controller != null && creature != null) {
|
||||
if (controller.flipCoin(game)) {
|
||||
return true;
|
||||
} else {
|
||||
if (!controller.flipCoin(game)) {
|
||||
creature.removeFromCombat(game);
|
||||
creature.setMaxBlocks(0);
|
||||
return true;
|
||||
|
||||
// Make blocked creatures unblocked
|
||||
BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<CombatGroup> combatGroups = watcher.getBlockedOnlyByCreature(creature.getId());
|
||||
if (combatGroups != null) {
|
||||
for (CombatGroup combatGroup : combatGroups) {
|
||||
if (combatGroup != null) {
|
||||
combatGroup.setBlocked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -99,4 +112,4 @@ class YdwenEfreetEffect extends OneShotEffect {
|
|||
public YdwenEfreetEffect copy() {
|
||||
return new YdwenEfreetEffect(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ public class Exodus extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Shield Mate", 19, Rarity.COMMON, mage.cards.s.ShieldMate.class));
|
||||
cards.add(new SetCardInfo("Skyshaper", 137, Rarity.UNCOMMON, mage.cards.s.Skyshaper.class));
|
||||
cards.add(new SetCardInfo("Skyshroud Elite", 123, Rarity.UNCOMMON, mage.cards.s.SkyshroudElite.class));
|
||||
cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class));
|
||||
cards.add(new SetCardInfo("Skyshroud War Beast", 124, Rarity.RARE, mage.cards.s.SkyshroudWarBeast.class));
|
||||
cards.add(new SetCardInfo("Slaughter", 74, Rarity.UNCOMMON, mage.cards.s.Slaughter.class));
|
||||
cards.add(new SetCardInfo("Soltari Visionary", 20, Rarity.COMMON, mage.cards.s.SoltariVisionary.class));
|
||||
cards.add(new SetCardInfo("Song of Serenity", 125, Rarity.UNCOMMON, mage.cards.s.SongOfSerenity.class));
|
||||
|
|
|
@ -159,6 +159,7 @@ public class IceAge extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Gravebind", 17, Rarity.RARE, mage.cards.g.Gravebind.class));
|
||||
cards.add(new SetCardInfo("Green Scarab", 252, Rarity.UNCOMMON, mage.cards.g.GreenScarab.class));
|
||||
cards.add(new SetCardInfo("Hallowed Ground", 253, Rarity.UNCOMMON, mage.cards.h.HallowedGround.class));
|
||||
cards.add(new SetCardInfo("Halls of Mist", 332, Rarity.RARE, mage.cards.h.HallsOfMist.class));
|
||||
cards.add(new SetCardInfo("Heal", 254, Rarity.COMMON, mage.cards.h.Heal.class));
|
||||
cards.add(new SetCardInfo("Hecatomb", 18, Rarity.RARE, mage.cards.h.Hecatomb.class));
|
||||
cards.add(new SetCardInfo("Hematite Talisman", 295, Rarity.UNCOMMON, mage.cards.h.HematiteTalisman.class));
|
||||
|
|
|
@ -128,6 +128,7 @@ public class Legends extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ghosts of the Damned", 12, Rarity.COMMON, mage.cards.g.GhostsOfTheDamned.class));
|
||||
cards.add(new SetCardInfo("Giant Strength", 147, Rarity.COMMON, mage.cards.g.GiantStrength.class));
|
||||
cards.add(new SetCardInfo("Giant Turtle", 102, Rarity.COMMON, mage.cards.g.GiantTurtle.class));
|
||||
cards.add(new SetCardInfo("Glyph of Destruction", 148, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class));
|
||||
cards.add(new SetCardInfo("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class));
|
||||
cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class));
|
||||
cards.add(new SetCardInfo("Greater Realm of Preservation", 187, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class));
|
||||
|
@ -148,6 +149,7 @@ public class Legends extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Hunding Gjornersen", 271, Rarity.UNCOMMON, mage.cards.h.HundingGjornersen.class));
|
||||
cards.add(new SetCardInfo("Hyperion Blacksmith", 150, Rarity.UNCOMMON, mage.cards.h.HyperionBlacksmith.class));
|
||||
cards.add(new SetCardInfo("Immolation", 151, Rarity.COMMON, mage.cards.i.Immolation.class));
|
||||
cards.add(new SetCardInfo("Imprison", 21, Rarity.RARE, mage.cards.i.Imprison.class));
|
||||
cards.add(new SetCardInfo("In the Eye of Chaos", 61, Rarity.RARE, mage.cards.i.InTheEyeOfChaos.class));
|
||||
cards.add(new SetCardInfo("Indestructible Aura", 190, Rarity.COMMON, mage.cards.i.IndestructibleAura.class));
|
||||
cards.add(new SetCardInfo("Infernal Medusa", 22, Rarity.UNCOMMON, mage.cards.i.InfernalMedusa.class));
|
||||
|
@ -219,6 +221,7 @@ public class Legends extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Red Mana Battery", 236, Rarity.UNCOMMON, mage.cards.r.RedManaBattery.class));
|
||||
cards.add(new SetCardInfo("Reincarnation", 115, Rarity.UNCOMMON, mage.cards.r.Reincarnation.class));
|
||||
cards.add(new SetCardInfo("Relic Barrier", 237, Rarity.UNCOMMON, mage.cards.r.RelicBarrier.class));
|
||||
cards.add(new SetCardInfo("Remove Enchantments", 202, Rarity.COMMON, mage.cards.r.RemoveEnchantments.class));
|
||||
cards.add(new SetCardInfo("Remove Soul", 72, Rarity.COMMON, mage.cards.r.RemoveSoul.class));
|
||||
cards.add(new SetCardInfo("Reset", 73, Rarity.UNCOMMON, mage.cards.r.Reset.class));
|
||||
cards.add(new SetCardInfo("Revelation", 116, Rarity.RARE, mage.cards.r.Revelation.class));
|
||||
|
|
|
@ -211,6 +211,7 @@ public class LimitedEditionAlpha extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class));
|
||||
cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class));
|
||||
cards.add(new SetCardInfo("Purelace", 216, Rarity.RARE, mage.cards.p.Purelace.class));
|
||||
cards.add(new SetCardInfo("Raging River", 169, Rarity.RARE, mage.cards.r.RagingRiver.class));
|
||||
cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class));
|
||||
cards.add(new SetCardInfo("Red Elemental Blast", 170, Rarity.COMMON, mage.cards.r.RedElementalBlast.class));
|
||||
cards.add(new SetCardInfo("Red Ward", 217, Rarity.UNCOMMON, mage.cards.r.RedWard.class));
|
||||
|
|
|
@ -216,6 +216,7 @@ public class LimitedEditionBeta extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class));
|
||||
cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class));
|
||||
cards.add(new SetCardInfo("Purelace", 218, Rarity.RARE, mage.cards.p.Purelace.class));
|
||||
cards.add(new SetCardInfo("Raging River", 170, Rarity.RARE, mage.cards.r.RagingRiver.class));
|
||||
cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class));
|
||||
cards.add(new SetCardInfo("Red Elemental Blast", 171, Rarity.COMMON, mage.cards.r.RedElementalBlast.class));
|
||||
cards.add(new SetCardInfo("Red Ward", 219, Rarity.UNCOMMON, mage.cards.r.RedWard.class));
|
||||
|
|
|
@ -93,6 +93,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ceremonial Guard", 182, Rarity.COMMON, mage.cards.c.CeremonialGuard.class));
|
||||
cards.add(new SetCardInfo("Chambered Nautilus", 64, Rarity.UNCOMMON, mage.cards.c.ChamberedNautilus.class));
|
||||
cards.add(new SetCardInfo("Charisma", 66, Rarity.RARE, mage.cards.c.Charisma.class));
|
||||
cards.add(new SetCardInfo("Charm Peddler", 6, Rarity.COMMON, mage.cards.c.CharmPeddler.class));
|
||||
cards.add(new SetCardInfo("Charmed Griffin", 7, Rarity.UNCOMMON, mage.cards.c.CharmedGriffin.class));
|
||||
cards.add(new SetCardInfo("Cho-Arrim Alchemist", 8, Rarity.RARE, mage.cards.c.ChoArrimAlchemist.class));
|
||||
cards.add(new SetCardInfo("Cho-Arrim Bruiser", 9, Rarity.RARE, mage.cards.c.ChoArrimBruiser.class));
|
||||
|
@ -123,7 +124,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Deadly Insect", 238, Rarity.COMMON, mage.cards.d.DeadlyInsect.class));
|
||||
cards.add(new SetCardInfo("Deathgazer", 130, Rarity.UNCOMMON, mage.cards.d.Deathgazer.class));
|
||||
cards.add(new SetCardInfo("Deepwood Drummer", 239, Rarity.COMMON, mage.cards.d.DeepwoodDrummer.class));
|
||||
cards.add(new SetCardInfo("Deepwood Elder", 240, Rarity.RARE, mage.cards.d.DeepwoodElder.class));
|
||||
cards.add(new SetCardInfo("Deepwood Elder", 240, Rarity.RARE, mage.cards.d.DeepwoodElder.class));
|
||||
cards.add(new SetCardInfo("Deepwood Ghoul", 131, Rarity.COMMON, mage.cards.d.DeepwoodGhoul.class));
|
||||
cards.add(new SetCardInfo("Deepwood Legate", 132, Rarity.UNCOMMON, mage.cards.d.DeepwoodLegate.class));
|
||||
cards.add(new SetCardInfo("Deepwood Tantiv", 241, Rarity.UNCOMMON, mage.cards.d.DeepwoodTantiv.class));
|
||||
|
@ -175,6 +176,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("High Market", 320, Rarity.RARE, mage.cards.h.HighMarket.class));
|
||||
cards.add(new SetCardInfo("High Seas", 83, Rarity.UNCOMMON, mage.cards.h.HighSeas.class));
|
||||
cards.add(new SetCardInfo("Highway Robber", 139, Rarity.COMMON, mage.cards.h.HighwayRobber.class));
|
||||
cards.add(new SetCardInfo("Hired Giant", 194, Rarity.UNCOMMON, mage.cards.h.HiredGiant.class));
|
||||
cards.add(new SetCardInfo("Honor the Fallen", 21, Rarity.RARE, mage.cards.h.HonorTheFallen.class));
|
||||
cards.add(new SetCardInfo("Hoodwink", 84, Rarity.COMMON, mage.cards.h.Hoodwink.class));
|
||||
cards.add(new SetCardInfo("Horned Troll", 251, Rarity.COMMON, mage.cards.h.HornedTroll.class));
|
||||
|
@ -186,6 +188,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Instigator", 140, Rarity.RARE, mage.cards.i.Instigator.class));
|
||||
cards.add(new SetCardInfo("Intimidation", 142, Rarity.UNCOMMON, mage.cards.i.Intimidation.class));
|
||||
cards.add(new SetCardInfo("Invigorate", 254, Rarity.COMMON, mage.cards.i.Invigorate.class));
|
||||
cards.add(new SetCardInfo("Inviolability", 23, Rarity.COMMON, mage.cards.i.Inviolability.class));
|
||||
cards.add(new SetCardInfo("Iron Lance", 300, Rarity.UNCOMMON, mage.cards.i.IronLance.class));
|
||||
cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
|
||||
|
@ -259,6 +262,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Rampart Crawler", 156, Rarity.COMMON, mage.cards.r.RampartCrawler.class));
|
||||
cards.add(new SetCardInfo("Rappelling Scouts", 41, Rarity.RARE, mage.cards.r.RappellingScouts.class));
|
||||
cards.add(new SetCardInfo("Remote Farm", 323, Rarity.COMMON, mage.cards.r.RemoteFarm.class));
|
||||
cards.add(new SetCardInfo("Renounce", 42, Rarity.UNCOMMON, mage.cards.r.Renounce.class));
|
||||
cards.add(new SetCardInfo("Reverent Mantra", 44, Rarity.RARE, mage.cards.r.ReverentMantra.class));
|
||||
cards.add(new SetCardInfo("Revive", 262, Rarity.UNCOMMON, mage.cards.r.Revive.class));
|
||||
cards.add(new SetCardInfo("Righteous Aura", 45, Rarity.UNCOMMON, mage.cards.r.RighteousAura.class));
|
||||
|
@ -281,9 +285,11 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Sand Squid", 96, Rarity.RARE, mage.cards.s.SandSquid.class));
|
||||
cards.add(new SetCardInfo("Sandstone Needle", 326, Rarity.COMMON, mage.cards.s.SandstoneNeedle.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Bailiff", 97, Rarity.RARE, mage.cards.s.SaprazzanBailiff.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Breaker", 98, Rarity.UNCOMMON, mage.cards.s.SaprazzanBreaker.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Cove", 327, Rarity.UNCOMMON, mage.cards.s.SaprazzanCove.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Heir", 99, Rarity.RARE, mage.cards.s.SaprazzanHeir.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Legate", 100, Rarity.UNCOMMON, mage.cards.s.SaprazzanLegate.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Outrigger", 101, Rarity.COMMON, mage.cards.s.SaprazzanOutrigger.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Raider", 102, Rarity.COMMON, mage.cards.s.SaprazzanRaider.class));
|
||||
cards.add(new SetCardInfo("Saprazzan Skerry", 328, Rarity.COMMON, mage.cards.s.SaprazzanSkerry.class));
|
||||
cards.add(new SetCardInfo("Scandalmonger", 158, Rarity.UNCOMMON, mage.cards.s.Scandalmonger.class));
|
||||
|
@ -341,6 +347,7 @@ public class MercadianMasques extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Toymaker", 314, Rarity.UNCOMMON, mage.cards.t.Toymaker.class));
|
||||
cards.add(new SetCardInfo("Trade Routes", 112, Rarity.RARE, mage.cards.t.TradeRoutes.class));
|
||||
cards.add(new SetCardInfo("Tranquility", 280, Rarity.COMMON, mage.cards.t.Tranquility.class));
|
||||
cards.add(new SetCardInfo("Trap Runner", 55, Rarity.UNCOMMON, mage.cards.t.TrapRunner.class));
|
||||
cards.add(new SetCardInfo("Tremor", 220, Rarity.COMMON, mage.cards.t.Tremor.class));
|
||||
cards.add(new SetCardInfo("Two-Headed Dragon", 221, Rarity.RARE, mage.cards.t.TwoHeadedDragon.class));
|
||||
cards.add(new SetCardInfo("Undertaker", 167, Rarity.COMMON, mage.cards.u.Undertaker.class));
|
||||
|
|
|
@ -260,6 +260,7 @@ public class Tempest extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class));
|
||||
cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class));
|
||||
cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class));
|
||||
cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class));
|
||||
cards.add(new SetCardInfo("Ruby Medallion", 295, Rarity.RARE, mage.cards.r.RubyMedallion.class));
|
||||
cards.add(new SetCardInfo("Sacred Guide", 250, Rarity.RARE, mage.cards.s.SacredGuide.class));
|
||||
cards.add(new SetCardInfo("Sadistic Glee", 47, Rarity.COMMON, mage.cards.s.SadisticGlee.class));
|
||||
|
|
|
@ -72,6 +72,7 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Coal Golem", 96, Rarity.UNCOMMON, mage.cards.c.CoalGolem.class));
|
||||
cards.add(new SetCardInfo("Dance of Many", 21, Rarity.RARE, mage.cards.d.DanceOfMany.class));
|
||||
cards.add(new SetCardInfo("Dark Heart of the Wood", 117, Rarity.COMMON, mage.cards.d.DarkHeartOfTheWood.class));
|
||||
cards.add(new SetCardInfo("Dark Sphere", 97, Rarity.RARE, mage.cards.d.DarkSphere.class));
|
||||
cards.add(new SetCardInfo("Deep Water", 22, Rarity.COMMON, mage.cards.d.DeepWater.class));
|
||||
cards.add(new SetCardInfo("Diabolic Machine", 98, Rarity.UNCOMMON, mage.cards.d.DiabolicMachine.class));
|
||||
cards.add(new SetCardInfo("Drowned", 23, Rarity.COMMON, mage.cards.d.Drowned.class));
|
||||
|
@ -120,6 +121,7 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Riptide", 34, Rarity.COMMON, mage.cards.r.Riptide.class));
|
||||
cards.add(new SetCardInfo("Safe Haven", 115, Rarity.RARE, mage.cards.s.SafeHaven.class));
|
||||
cards.add(new SetCardInfo("Savaen Elves", 47, Rarity.COMMON, mage.cards.s.SavaenElves.class));
|
||||
cards.add(new SetCardInfo("Scarecrow", 105, Rarity.RARE, mage.cards.s.Scarecrow.class));
|
||||
cards.add(new SetCardInfo("Scarwood Bandits", 48, Rarity.RARE, mage.cards.s.ScarwoodBandits.class));
|
||||
cards.add(new SetCardInfo("Scarwood Goblins", 119, Rarity.COMMON, mage.cards.s.ScarwoodGoblins.class));
|
||||
cards.add(new SetCardInfo("Scarwood Hag", 49, Rarity.UNCOMMON, mage.cards.s.ScarwoodHag.class));
|
||||
|
@ -133,6 +135,7 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Sunken City", 35, Rarity.COMMON, mage.cards.s.SunkenCity.class));
|
||||
cards.add(new SetCardInfo("Tivadar's Crusade", 91, Rarity.UNCOMMON, mage.cards.t.TivadarsCrusade.class));
|
||||
cards.add(new SetCardInfo("Tormod's Crypt", 109, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
|
||||
cards.add(new SetCardInfo("Tower of Coireall", 110, Rarity.RARE, mage.cards.t.TowerOfCoireall.class));
|
||||
cards.add(new SetCardInfo("Tracker", 52, Rarity.RARE, mage.cards.t.Tracker.class));
|
||||
cards.add(new SetCardInfo("Uncle Istvan", 16, Rarity.UNCOMMON, mage.cards.u.UncleIstvan.class));
|
||||
cards.add(new SetCardInfo("Venom", 53, Rarity.COMMON, mage.cards.v.Venom.class));
|
||||
|
|
|
@ -216,6 +216,7 @@ public class UnlimitedEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Psionic Blast", 75, Rarity.UNCOMMON, mage.cards.p.PsionicBlast.class));
|
||||
cards.add(new SetCardInfo("Psychic Venom", 76, Rarity.COMMON, mage.cards.p.PsychicVenom.class));
|
||||
cards.add(new SetCardInfo("Purelace", 217, Rarity.RARE, mage.cards.p.Purelace.class));
|
||||
cards.add(new SetCardInfo("Raging River", 169, Rarity.RARE, mage.cards.r.RagingRiver.class));
|
||||
cards.add(new SetCardInfo("Raise Dead", 31, Rarity.COMMON, mage.cards.r.RaiseDead.class));
|
||||
cards.add(new SetCardInfo("Red Elemental Blast", 170, Rarity.COMMON, mage.cards.r.RedElementalBlast.class));
|
||||
cards.add(new SetCardInfo("Red Ward", 218, Rarity.UNCOMMON, mage.cards.r.RedWard.class));
|
||||
|
|
|
@ -118,6 +118,7 @@ public class UrzasSaga extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Darkest Hour", 128, Rarity.RARE, mage.cards.d.DarkestHour.class));
|
||||
cards.add(new SetCardInfo("Dark Hatchling", 126, Rarity.RARE, mage.cards.d.DarkHatchling.class));
|
||||
cards.add(new SetCardInfo("Dark Ritual", 127, Rarity.COMMON, mage.cards.d.DarkRitual.class));
|
||||
cards.add(new SetCardInfo("Defensive Formation", 9, Rarity.UNCOMMON, mage.cards.d.DefensiveFormation.class));
|
||||
cards.add(new SetCardInfo("Despondency", 129, Rarity.COMMON, mage.cards.d.Despondency.class));
|
||||
cards.add(new SetCardInfo("Destructive Urge", 180, Rarity.UNCOMMON, mage.cards.d.DestructiveUrge.class));
|
||||
cards.add(new SetCardInfo("Diabolic Servitude", 130, Rarity.UNCOMMON, mage.cards.d.DiabolicServitude.class));
|
||||
|
@ -362,7 +363,7 @@ public class UrzasSaga extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Vug Lizard", 227, Rarity.UNCOMMON, mage.cards.v.VugLizard.class));
|
||||
cards.add(new SetCardInfo("War Dance", 282, Rarity.UNCOMMON, mage.cards.w.WarDance.class));
|
||||
cards.add(new SetCardInfo("Wall of Junk", 315, Rarity.UNCOMMON, mage.cards.w.WallOfJunk.class));
|
||||
cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class));
|
||||
cards.add(new SetCardInfo("Waylay", 56, Rarity.UNCOMMON, mage.cards.w.Waylay.class));
|
||||
cards.add(new SetCardInfo("Western Paladin", 168, Rarity.RARE, mage.cards.w.WesternPaladin.class));
|
||||
cards.add(new SetCardInfo("Whetstone", 316, Rarity.RARE, mage.cards.w.Whetstone.class));
|
||||
cards.add(new SetCardInfo("Whirlwind", 283, Rarity.RARE, mage.cards.w.Whirlwind.class));
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
* @author L_J
|
||||
*/
|
||||
public class ControllerAssignCombatDamageToBlockersAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final ControllerAssignCombatDamageToBlockersAbility instance = new ControllerAssignCombatDamageToBlockersAbility();
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ControllerAssignCombatDamageToBlockersAbility getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ControllerAssignCombatDamageToBlockersAbility() {
|
||||
super(AbilityType.STATIC, Zone.BATTLEFIELD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Rather than the attacking player, you assign the combat damage of each creature attacking you. You can divide that creature's combat damage as you choose among any of the creatures blocking it.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerAssignCombatDamageToBlockersAbility copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
|
@ -78,6 +78,10 @@ public abstract class RequirementEffect extends ContinuousEffectImpl {
|
|||
public boolean mustBlockAny(Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean mustBlockAllAttackers(Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the defender a attacker has to attack
|
||||
|
|
|
@ -16,6 +16,7 @@ import mage.util.CardUtil;
|
|||
public class DoIfCostPaid extends OneShotEffect {
|
||||
|
||||
protected Effects executingEffects = new Effects();
|
||||
protected Effects otherwiseEffects = new Effects(); // used for Imprison
|
||||
private final Cost cost;
|
||||
private String chooseUseText;
|
||||
private boolean optional;
|
||||
|
@ -24,6 +25,11 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
this(effect, cost, null);
|
||||
}
|
||||
|
||||
public DoIfCostPaid(Effect effect, Effect effect2, Cost cost) {
|
||||
this(effect, cost, null, true);
|
||||
this.otherwiseEffects.add(effect2);
|
||||
}
|
||||
|
||||
public DoIfCostPaid(Effect effect, Cost cost, String chooseUseText) {
|
||||
this(effect, cost, chooseUseText, true);
|
||||
}
|
||||
|
@ -39,6 +45,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
public DoIfCostPaid(final DoIfCostPaid effect) {
|
||||
super(effect);
|
||||
this.executingEffects = effect.executingEffects.copy();
|
||||
this.otherwiseEffects = effect.otherwiseEffects.copy();
|
||||
this.cost = effect.cost.copy();
|
||||
this.chooseUseText = effect.chooseUseText;
|
||||
this.optional = effect.optional;
|
||||
|
@ -80,6 +87,26 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
}
|
||||
player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek
|
||||
}
|
||||
else if (!otherwiseEffects.isEmpty()) {
|
||||
for (Effect effect : otherwiseEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) effect, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!otherwiseEffects.isEmpty()) {
|
||||
for (Effect effect : otherwiseEffects) {
|
||||
effect.setTargetPointer(this.targetPointer);
|
||||
if (effect instanceof OneShotEffect) {
|
||||
result &= effect.apply(game, source);
|
||||
} else {
|
||||
game.addEffect((ContinuousEffect) effect, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -99,7 +126,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
if (!staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
return (optional ? "you may " : "") + getCostText() + ". If you do, " + executingEffects.getText(mode);
|
||||
return (optional ? "you may " : "") + getCostText() + ". If you do, " + executingEffects.getText(mode) + (!otherwiseEffects.isEmpty() ? " If you don't, " + otherwiseEffects.getText(mode) : "");
|
||||
}
|
||||
|
||||
protected String getCostText() {
|
||||
|
|
|
@ -1201,6 +1201,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
for (CombatGroup group : groups) {
|
||||
result |= group.remove(creatureId);
|
||||
}
|
||||
blockingGroups.remove(creatureId);
|
||||
if (result && withInfo) {
|
||||
game.informPlayers(creature.getLogName() + " removed from combat");
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
|
||||
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
|
||||
import mage.abilities.keyword.CantBlockAloneAbility;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
|
@ -244,6 +245,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
blocker.markDamage(damage, attacker.getId(), game, true, true);
|
||||
} else {
|
||||
Player player = game.getPlayer(attacker.getControllerId());
|
||||
for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation
|
||||
if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) {
|
||||
player = game.getPlayer(defendingPlayerId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game);
|
||||
blocker.markDamage(damageAssigned, attacker.getId(), game, true, true);
|
||||
damage -= damageAssigned;
|
||||
|
@ -269,7 +276,15 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
if (attacker == null) {
|
||||
return;
|
||||
}
|
||||
boolean oldRuleDamage = false;
|
||||
Player player = game.getPlayer(attacker.getControllerId());
|
||||
for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation
|
||||
if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) {
|
||||
player = game.getPlayer(defendingPlayerId);
|
||||
oldRuleDamage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int damage = getDamageValueFromPermanent(attacker, game);
|
||||
if (canDamage(attacker, first)) {
|
||||
// must be set before attacker damage marking because of effects like Test of Faith
|
||||
|
@ -284,6 +299,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
}
|
||||
Map<UUID, Integer> assigned = new HashMap<>();
|
||||
if (blocked) {
|
||||
boolean excessDamageToDefender = true;
|
||||
for (UUID blockerId : blockerOrder) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null) {
|
||||
|
@ -298,15 +314,23 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
damage = 0;
|
||||
break;
|
||||
}
|
||||
int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game);
|
||||
int damageAssigned = 0;
|
||||
if (!oldRuleDamage) {
|
||||
damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game);
|
||||
} else {
|
||||
damageAssigned = player.getAmount(0, damage, "Assign damage to " + blocker.getName(), game);
|
||||
if (damageAssigned < lethalDamage) {
|
||||
excessDamageToDefender = false; // all blockers need to have lethal damage assigned before it can trample over to the defender
|
||||
}
|
||||
}
|
||||
assigned.put(blockerId, damageAssigned);
|
||||
damage -= damageAssigned;
|
||||
}
|
||||
}
|
||||
if (damage > 0 && hasTrample(attacker)) {
|
||||
if (damage > 0 && hasTrample(attacker) && excessDamageToDefender) {
|
||||
defenderDamage(attacker, damage, game);
|
||||
} else if (!blockerOrder.isEmpty()) {
|
||||
// Assign the damge left to first blocker
|
||||
// Assign the damage left to first blocker
|
||||
assigned.put(blockerOrder.get(0), assigned.get(blockerOrder.get(0)) + damage);
|
||||
}
|
||||
}
|
||||
|
@ -382,6 +406,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
}
|
||||
if (lethalDamage >= damage) {
|
||||
assigned.put(attackerId, damage);
|
||||
damage = 0;
|
||||
break;
|
||||
}
|
||||
int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + attacker.getName(), game);
|
||||
|
@ -389,6 +414,10 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
damage -= damageAssigned;
|
||||
}
|
||||
}
|
||||
if (damage > 0) {
|
||||
// Assign the damage left to first attacker
|
||||
assigned.put(attackerOrder.get(0), assigned.get(attackerOrder.get(0)) + damage);
|
||||
}
|
||||
|
||||
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
|
||||
Permanent attacker = game.getPermanent(entry.getKey());
|
||||
|
@ -463,6 +492,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
return;
|
||||
}
|
||||
Player player = game.getPlayer(playerId);
|
||||
for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) { // for handling Defensive Formation
|
||||
if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) {
|
||||
player = game.getPlayer(defendingPlayerId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<UUID> blockerList = new ArrayList<>(blockers);
|
||||
blockerOrder.clear();
|
||||
while (player.canRespond()) {
|
||||
|
@ -532,6 +567,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
if (attackers.contains(creatureId)) {
|
||||
attackers.remove(creatureId);
|
||||
result = true;
|
||||
if (attackerOrder.contains(creatureId)) {
|
||||
attackerOrder.remove(creatureId);
|
||||
}
|
||||
} else if (blockers.contains(creatureId)) {
|
||||
blockers.remove(creatureId);
|
||||
result = true;
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.watchers.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class AttackedLastTurnWatcher extends Watcher {
|
||||
|
||||
public final Map<UUID, Set<MageObjectReference>> attackedLastTurnCreatures = new HashMap<>(); // Map<lastTurnOfPlayerId, Set<attackingCreature>>
|
||||
public final Map<UUID, Set<MageObjectReference>> attackedThisTurnCreatures = new HashMap<>(); // dummy map for beginning of turn iteration purposes
|
||||
|
||||
public AttackedLastTurnWatcher() {
|
||||
super(AttackedLastTurnWatcher.class.getSimpleName(), WatcherScope.GAME);
|
||||
}
|
||||
|
||||
public AttackedLastTurnWatcher(final AttackedLastTurnWatcher watcher) {
|
||||
super(watcher);
|
||||
for (Entry<UUID, Set<MageObjectReference>> entry : watcher.attackedLastTurnCreatures.entrySet()) {
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>();
|
||||
allAttackersCopy.addAll(entry.getValue());
|
||||
attackedLastTurnCreatures.put(entry.getKey(), allAttackersCopy);
|
||||
}
|
||||
for (Entry<UUID, Set<MageObjectReference>> entry : watcher.attackedThisTurnCreatures.entrySet()) {
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>();
|
||||
allAttackersCopy.addAll(entry.getValue());
|
||||
attackedThisTurnCreatures.put(entry.getKey(), allAttackersCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) {
|
||||
UUID activePlayer = game.getActivePlayerId();
|
||||
if (attackedThisTurnCreatures.containsKey(activePlayer)) {
|
||||
if (attackedThisTurnCreatures.get(activePlayer) != null) {
|
||||
attackedLastTurnCreatures.put(activePlayer, getAttackedThisTurnCreatures(activePlayer));
|
||||
}
|
||||
attackedThisTurnCreatures.remove(activePlayer);
|
||||
} else { // } else if (attackedLastTurnCreatures.containsKey(activePlayer)) {
|
||||
attackedLastTurnCreatures.remove(activePlayer);
|
||||
}
|
||||
}
|
||||
if (event.getType() == GameEvent.EventType.DECLARED_ATTACKERS) {
|
||||
UUID attackingPlayer = game.getCombat().getAttackingPlayerId();
|
||||
Set<MageObjectReference> attackingCreatures = getAttackedThisTurnCreatures(attackingPlayer);
|
||||
for (UUID attackerId : game.getCombat().getAttackers()) {
|
||||
Permanent attacker = game.getPermanent(attackerId);
|
||||
if (attacker != null) {
|
||||
attackingCreatures.add(new MageObjectReference(attacker, game));
|
||||
}
|
||||
}
|
||||
attackedThisTurnCreatures.put(attackingPlayer, attackingCreatures);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<MageObjectReference> getAttackedLastTurnCreatures(UUID combatPlayerId) {
|
||||
if (attackedLastTurnCreatures.get(combatPlayerId) != null) {
|
||||
return attackedLastTurnCreatures.get(combatPlayerId);
|
||||
}
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
public Set<MageObjectReference> getAttackedThisTurnCreatures(UUID combatPlayerId) {
|
||||
if (attackedThisTurnCreatures.get(combatPlayerId) != null) {
|
||||
return attackedThisTurnCreatures.get(combatPlayerId);
|
||||
}
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttackedLastTurnWatcher copy() {
|
||||
return new AttackedLastTurnWatcher(this);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue