mirror of
https://github.com/correl/mage.git
synced 2024-12-26 11:09:27 +00:00
Merge pull request #22 from magefree/master
Merge https://github.com/magefree/mage
This commit is contained in:
commit
6454033c5a
52 changed files with 2130 additions and 612 deletions
|
@ -141,6 +141,18 @@
|
|||
<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 -->
|
||||
<dependency>
|
||||
<groupId>org.ocpsoft.prettytime</groupId>
|
||||
<artifactId>prettytime</artifactId>
|
||||
<version>3.2.7.Final</version>
|
||||
</dependency>
|
||||
</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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import mage.cards.decks.importer.DeckImporterUtil;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.SessionHandler;
|
||||
|
@ -54,7 +57,6 @@ import mage.client.components.MageComponents;
|
|||
import mage.client.dialog.*;
|
||||
import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER;
|
||||
import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH;
|
||||
import static mage.client.table.TablesPanel.PASSWORDED;
|
||||
import mage.client.util.ButtonColumn;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.IgnoreList;
|
||||
|
@ -70,6 +72,9 @@ import mage.view.RoomUsersView;
|
|||
import mage.view.TableView;
|
||||
import mage.view.UserRequestMessage;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.ocpsoft.prettytime.PrettyTime;
|
||||
import org.ocpsoft.prettytime.units.JustNow;
|
||||
import org.ocpsoft.prettytime.Duration;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -80,7 +85,6 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
private static final Logger LOGGER = Logger.getLogger(TablesPanel.class);
|
||||
private static final int[] DEFAULT_COLUMNS_WIDTH = {35, 150, 120, 180, 80, 120, 80, 60, 40, 40, 60};
|
||||
|
||||
public static final String PASSWORDED = "***";
|
||||
private final TableTableModel tableModel;
|
||||
private final MatchesTableModel matchesModel;
|
||||
private UUID roomId;
|
||||
|
@ -94,12 +98,62 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
private java.util.List<String> messages;
|
||||
private int currentMessage;
|
||||
private final MageTableRowSorter activeTablesSorter;
|
||||
private final MageTableRowSorter completedTablesSorter;
|
||||
|
||||
private final ButtonColumn actionButton1;
|
||||
private final ButtonColumn actionButton2;
|
||||
|
||||
final JToggleButton[] filterButtons;
|
||||
|
||||
// time formater
|
||||
private PrettyTime timeFormater = new PrettyTime();
|
||||
|
||||
// time ago renderer
|
||||
TableCellRenderer timeAgoCellRenderer = new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
Date d = (Date)value;
|
||||
label.setText(timeFormater.format(d));
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
// duration renderer
|
||||
TableCellRenderer durationCellRenderer = new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
Long ms = (Long)value;
|
||||
|
||||
if(ms != 0){
|
||||
Duration dur = timeFormater.approximateDuration(new Date(ms));
|
||||
label.setText((timeFormater.formatDuration(dur)));
|
||||
}else{
|
||||
label.setText("");
|
||||
}
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
// datetime render
|
||||
TableCellRenderer datetimeCellRenderer = new DefaultTableCellRenderer() {
|
||||
DateFormat datetimeFormater = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
Date d = (Date)value;
|
||||
if(d != null){
|
||||
label.setText(datetimeFormater.format(d));
|
||||
}else{
|
||||
label.setText("");
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates new form TablesPanel
|
||||
*/
|
||||
|
@ -112,20 +166,56 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
initComponents();
|
||||
// tableModel.setSession(session);
|
||||
|
||||
tableTables.createDefaultColumnsFromModel();
|
||||
// formater
|
||||
timeFormater.setLocale(Locale.ENGLISH);
|
||||
JustNow jn = timeFormater.getUnit(JustNow.class);
|
||||
jn.setMaxQuantity(1000L * 30L); // 30 seconds gap (show "just now" from 0 to 30 secs)
|
||||
|
||||
// 1. TABLE CURRENT
|
||||
tableTables.createDefaultColumnsFromModel();
|
||||
activeTablesSorter = new MageTableRowSorter(tableModel);
|
||||
tableTables.setRowSorter(activeTablesSorter);
|
||||
|
||||
// time ago
|
||||
tableTables.getColumnModel().getColumn(TableTableModel.COLUMN_CREATED).setCellRenderer(timeAgoCellRenderer);
|
||||
/* date sorter (not need, default is good - see getColumnClass)
|
||||
activeTablesSorter.setComparator(TableTableModel.COLUMN_CREATED, new Comparator<Date>() {
|
||||
@Override
|
||||
public int compare(Date v1, Date v2) {
|
||||
return v1.compareTo(v2);
|
||||
}
|
||||
|
||||
});*/
|
||||
// default sort by created date (last games from above)
|
||||
ArrayList list = new ArrayList();
|
||||
list.add(new RowSorter.SortKey(TableTableModel.COLUMN_CREATED, SortOrder.DESCENDING));
|
||||
activeTablesSorter.setSortKeys(list);
|
||||
|
||||
TableUtil.setColumnWidthAndOrder(tableTables, DEFAULT_COLUMNS_WIDTH,
|
||||
PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, PreferencesDialog.KEY_TABLES_COLUMNS_ORDER);
|
||||
PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, PreferencesDialog.KEY_TABLES_COLUMNS_ORDER); // TODO: is sort order save and restore after app restart/window open?
|
||||
|
||||
tableCompleted.setRowSorter(new MageTableRowSorter(matchesModel));
|
||||
|
||||
|
||||
// 2. TABLE COMPLETED
|
||||
completedTablesSorter = new MageTableRowSorter(matchesModel);
|
||||
tableCompleted.setRowSorter(completedTablesSorter);
|
||||
|
||||
// duration
|
||||
tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_DURATION).setCellRenderer(durationCellRenderer);
|
||||
// start-end
|
||||
tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_START).setCellRenderer(datetimeCellRenderer);
|
||||
tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_END).setCellRenderer(datetimeCellRenderer);
|
||||
// default sort by ended date (last games from above)
|
||||
ArrayList list2 = new ArrayList();
|
||||
list2.add(new RowSorter.SortKey(MatchesTableModel.COLUMN_END, SortOrder.DESCENDING));
|
||||
completedTablesSorter.setSortKeys(list2);
|
||||
|
||||
// 3. CHAT
|
||||
chatPanelMain.getUserChatPanel().useExtendedView(ChatPanelBasic.VIEW_MODE.NONE);
|
||||
chatPanelMain.getUserChatPanel().setBorder(null);
|
||||
chatPanelMain.getUserChatPanel().setChatType(ChatPanelBasic.ChatType.TABLES);
|
||||
|
||||
// 4. BUTTONS
|
||||
filterButtons = new JToggleButton[]{btnStateWaiting, btnStateActive, btnStateFinished,
|
||||
btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited,
|
||||
btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther,
|
||||
|
@ -181,7 +271,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
if (isTournament) {
|
||||
LOGGER.info("Joining tournament " + tableId);
|
||||
if (deckType.startsWith("Limited")) {
|
||||
if (PASSWORDED.equals(pwdColumn)) {
|
||||
if (TableTableModel.PASSWORD_VALUE_YES.equals(pwdColumn)) {
|
||||
joinTableDialog.showDialog(roomId, tableId, true, deckType.startsWith("Limited"));
|
||||
} else {
|
||||
SessionHandler.joinTournamentTable(roomId, tableId, SessionHandler.getUserName(), PlayerType.HUMAN, 1, null, "");
|
||||
|
@ -225,7 +315,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int modelRow = Integer.valueOf(e.getActionCommand());
|
||||
String action = (String) matchesModel.getValueAt(modelRow, MatchesTableModel.ACTION_COLUMN);
|
||||
String action = (String) matchesModel.getValueAt(modelRow, MatchesTableModel.COLUMN_ACTION);
|
||||
switch (action) {
|
||||
case "Replay":
|
||||
java.util.List<UUID> gameList = matchesModel.getListofGames(modelRow);
|
||||
|
@ -250,7 +340,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
|
||||
// !!!! adds action buttons to the table panel (don't delete this)
|
||||
actionButton1 = new ButtonColumn(tableTables, openTableAction, tableTables.convertColumnIndexToView(TableTableModel.ACTION_COLUMN));
|
||||
actionButton2 = new ButtonColumn(tableCompleted, closedTableAction, tableCompleted.convertColumnIndexToView(MatchesTableModel.ACTION_COLUMN));
|
||||
actionButton2 = new ButtonColumn(tableCompleted, closedTableAction, tableCompleted.convertColumnIndexToView(MatchesTableModel.COLUMN_ACTION));
|
||||
// !!!!
|
||||
}
|
||||
|
||||
|
@ -583,21 +673,27 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
skillFilterList.add(RowFilter.regexFilter(SkillLevel.SERIOUS.toString(), TableTableModel.COLUMN_SKILL));
|
||||
}
|
||||
|
||||
String ratedMark = TableTableModel.RATED_VALUE_YES;
|
||||
java.util.List<RowFilter<Object, Object>> ratingFilterList = new ArrayList<>();
|
||||
if (btnRated.isSelected()) {
|
||||
ratingFilterList.add(RowFilter.regexFilter("^Rated", TableTableModel.COLUMN_RATING));
|
||||
// yes word
|
||||
ratingFilterList.add(RowFilter.regexFilter("^" + ratedMark, TableTableModel.COLUMN_RATING));
|
||||
}
|
||||
if (btnUnrated.isSelected()) {
|
||||
ratingFilterList.add(RowFilter.regexFilter("^Unrated", TableTableModel.COLUMN_RATING));
|
||||
// not yes word, see https://stackoverflow.com/a/406408/1276632
|
||||
ratingFilterList.add(RowFilter.regexFilter("^((?!" + ratedMark + ").)*$", TableTableModel.COLUMN_RATING));
|
||||
}
|
||||
|
||||
// Password
|
||||
String passwordMark = TableTableModel.PASSWORD_VALUE_YES;
|
||||
java.util.List<RowFilter<Object, Object>> passwordFilterList = new ArrayList<>();
|
||||
if (btnOpen.isSelected()) {
|
||||
passwordFilterList.add(RowFilter.regexFilter("^$", TableTableModel.COLUMN_PASSWORD));
|
||||
}
|
||||
if (btnPassword.isSelected()) {
|
||||
passwordFilterList.add(RowFilter.regexFilter("^\\*\\*\\*$", TableTableModel.COLUMN_PASSWORD));
|
||||
// yes
|
||||
passwordFilterList.add(RowFilter.regexFilter("^" + passwordMark, TableTableModel.COLUMN_PASSWORD));
|
||||
}
|
||||
if (btnOpen.isSelected()) {
|
||||
// no
|
||||
passwordFilterList.add(RowFilter.regexFilter("^((?!" + passwordMark + ").)*$", TableTableModel.COLUMN_PASSWORD));
|
||||
}
|
||||
|
||||
// Hide games of ignored players
|
||||
|
@ -1281,16 +1377,24 @@ class TableTableModel extends AbstractTableModel {
|
|||
public static final int COLUMN_QUIT_RATIO = 10;
|
||||
public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0)
|
||||
|
||||
public static final String RATED_VALUE_YES = "YES";
|
||||
public static final String RATED_VALUE_NO = "";
|
||||
|
||||
public static final String PASSWORD_VALUE_YES = "YES";
|
||||
|
||||
private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"};
|
||||
|
||||
private TableView[] tables = new TableView[0];
|
||||
private static final DateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
TableTableModel() {
|
||||
}
|
||||
|
||||
public void loadData(Collection<TableView> tables) throws MageRemoteException {
|
||||
this.tables = tables.toArray(new TableView[0]);
|
||||
this.fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return tables.length;
|
||||
|
@ -1317,13 +1421,13 @@ class TableTableModel extends AbstractTableModel {
|
|||
case 5:
|
||||
return tables[arg0].getTableStateText();
|
||||
case 6:
|
||||
return tables[arg0].isPassworded() ? PASSWORDED : "";
|
||||
return tables[arg0].isPassworded() ? PASSWORD_VALUE_YES : "";
|
||||
case 7:
|
||||
return timeFormatter.format(tables[arg0].getCreateTime());
|
||||
return tables[arg0].getCreateTime(); // use cell render, not format here
|
||||
case 8:
|
||||
return tables[arg0].getSkillLevel();
|
||||
case 9:
|
||||
return tables[arg0].isRated() ? "Rated" : "Unrated";
|
||||
return tables[arg0].isRated() ? RATED_VALUE_YES : RATED_VALUE_NO;
|
||||
case 10:
|
||||
return tables[arg0].getQuitRatio();
|
||||
case 11:
|
||||
|
@ -1384,6 +1488,8 @@ class TableTableModel extends AbstractTableModel {
|
|||
return Icon.class;
|
||||
case COLUMN_SKILL:
|
||||
return SkillLevel.class;
|
||||
case COLUMN_CREATED:
|
||||
return Date.class;
|
||||
default:
|
||||
return String.class;
|
||||
}
|
||||
|
@ -1486,17 +1592,22 @@ class UpdatePlayersTask extends SwingWorker<Void, Collection<RoomUsersView>> {
|
|||
|
||||
class MatchesTableModel extends AbstractTableModel {
|
||||
|
||||
public static final int ACTION_COLUMN = 7; // column the action is located (starting with 0)
|
||||
public static final int GAMES_LIST_COLUMN = 8;
|
||||
private final String[] columnNames = new String[]{"Deck Type", "Players", "Game Type", "Rating", "Result", "Start Time", "End Time", "Action"};
|
||||
private final String[] columnNames = new String[]{"Deck Type", "Players", "Game Type", "Rating", "Result", "Duration", "Start Time", "End Time", "Action"};
|
||||
public static final int COLUMN_DURATION = 5;
|
||||
public static final int COLUMN_START = 6;
|
||||
public static final int COLUMN_END = 7;
|
||||
public static final int COLUMN_ACTION = 8; // column the action is located (starting with 0)
|
||||
|
||||
private MatchView[] matches = new MatchView[0];
|
||||
private static final DateFormat timeFormatter = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
|
||||
|
||||
public void loadData(Collection<MatchView> matches) throws MageRemoteException {
|
||||
this.matches = matches.toArray(new MatchView[0]);
|
||||
this.fireTableDataChanged();
|
||||
}
|
||||
|
||||
MatchesTableModel(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return matches.length;
|
||||
|
@ -1517,22 +1628,20 @@ class MatchesTableModel extends AbstractTableModel {
|
|||
case 2:
|
||||
return matches[arg0].getGameType();
|
||||
case 3:
|
||||
return matches[arg0].isRated() ? "Rated" : "Unrated";
|
||||
return matches[arg0].isRated() ? TableTableModel.RATED_VALUE_YES : TableTableModel.RATED_VALUE_NO;
|
||||
case 4:
|
||||
return matches[arg0].getResult();
|
||||
case 5:
|
||||
if (matches[arg0].getStartTime() != null) {
|
||||
return timeFormatter.format(matches[arg0].getStartTime());
|
||||
if (matches[arg0].getEndTime() != null) {
|
||||
return matches[arg0].getEndTime().getTime() - matches[arg0].getStartTime().getTime() + new Date().getTime();
|
||||
} else {
|
||||
return "";
|
||||
return 0L;
|
||||
}
|
||||
case 6:
|
||||
if (matches[arg0].getEndTime() != null) {
|
||||
return timeFormatter.format(matches[arg0].getEndTime());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
return matches[arg0].getStartTime();
|
||||
case 7:
|
||||
return matches[arg0].getEndTime();
|
||||
case 8:
|
||||
if (matches[arg0].isTournament()) {
|
||||
return "Show";
|
||||
} else if (matches[arg0].isReplayAvailable()) {
|
||||
|
@ -1540,7 +1649,7 @@ class MatchesTableModel extends AbstractTableModel {
|
|||
} else {
|
||||
return "None";
|
||||
}
|
||||
case 8:
|
||||
case 9:
|
||||
return matches[arg0].getGames();
|
||||
}
|
||||
return "";
|
||||
|
@ -1575,12 +1684,21 @@ class MatchesTableModel extends AbstractTableModel {
|
|||
|
||||
@Override
|
||||
public Class getColumnClass(int columnIndex) {
|
||||
return String.class;
|
||||
switch (columnIndex) {
|
||||
case COLUMN_DURATION:
|
||||
return Long.class;
|
||||
case COLUMN_START:
|
||||
return Date.class;
|
||||
case COLUMN_END:
|
||||
return Date.class;
|
||||
default:
|
||||
return String.class;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return columnIndex == ACTION_COLUMN;
|
||||
return columnIndex == COLUMN_ACTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,13 @@
|
|||
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.awt.image.FilteredImageSource;
|
||||
import java.awt.image.ImageProducer;
|
||||
import java.awt.image.RGBImageFilter;
|
||||
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 +18,37 @@ 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.jdesktop.swingx.graphics.ShadowRenderer;
|
||||
import org.jdesktop.swingx.graphics.GraphicsUtilities;
|
||||
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 +75,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 +122,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 +137,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 +157,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 +181,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 +195,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 +214,253 @@ 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, boolean useShadow) 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);
|
||||
}
|
||||
// debug: disable shadow gen, need to test it
|
||||
useShadow = false;
|
||||
|
||||
// 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/
|
||||
|
||||
if (useShadow && ((resizeToWidth <= 0) || (resizeToHeight <= 0))){
|
||||
throw new IllegalArgumentException("Must use non zero sizes for shadow.");
|
||||
}
|
||||
|
||||
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
|
||||
int shadowX = 0;
|
||||
int shadowY = 0;
|
||||
if(useShadow) {
|
||||
// shadow size (16px image: 1px left, 2px bottom)
|
||||
shadowX = 1 * Math.round(1f / 16f * resizeToWidth);
|
||||
shadowY = 2 * Math.round(1f / 16f * resizeToHeight);
|
||||
resizeToWidth = resizeToWidth - shadowX;
|
||||
resizeToHeight = resizeToHeight - shadowY;
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
BufferedImage originImage = imagePointer[0];
|
||||
|
||||
if(useShadow && (originImage.getWidth() > 0)){
|
||||
// draw shadow
|
||||
// origin image was reduces in sizes to fit shadow
|
||||
// see https://stackoverflow.com/a/40833715/1276632
|
||||
|
||||
// a filter which converts all colors except 0 to black
|
||||
ImageProducer prod = new FilteredImageSource(originImage.getSource(), new RGBImageFilter() {
|
||||
@Override
|
||||
public int filterRGB(int x, int y, int rgb) {
|
||||
if (rgb == 0)
|
||||
return 0;
|
||||
else
|
||||
return 0xff000000;
|
||||
}
|
||||
});
|
||||
// create whe black image
|
||||
Image shadow = Toolkit.getDefaultToolkit().createImage(prod);
|
||||
// result
|
||||
BufferedImage result = new BufferedImage(originImage.getWidth() + shadowX, originImage.getHeight() + shadowY, originImage.getType());
|
||||
Graphics2D g = (Graphics2D) result.getGraphics();
|
||||
// draw shadow with offset (left bottom)
|
||||
g.drawImage(shadow, -1 * shadowX, shadowY, null);
|
||||
// draw original image
|
||||
g.drawImage(originImage, 0, 0, null);
|
||||
return result;
|
||||
}else{
|
||||
// return origin image without shadow
|
||||
return originImage;
|
||||
}
|
||||
|
||||
/*
|
||||
BufferedImage base = GraphicsUtilities.createCompatibleTranslucentImage(w, h);
|
||||
Graphics2D g2 = base.createGraphics();
|
||||
g2.setColor(Color.WHITE);
|
||||
g2.fillRoundRect(0, 0, image.getWidth(), image.getHeight(), 10, 10);
|
||||
g2.dispose();
|
||||
|
||||
ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f,
|
||||
Color.GRAY);
|
||||
return renderer.createShadow(base);
|
||||
*/
|
||||
|
||||
//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, true);
|
||||
|
||||
} 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 +474,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 +669,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 +702,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 +742,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 +767,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" + File.separator;
|
||||
} else {
|
||||
return imagesPath + "TOK" + File.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 + set + 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);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
|
|||
public final static int MAGE_VERSION_MAJOR = 1;
|
||||
public final static int MAGE_VERSION_MINOR = 4;
|
||||
public final static int MAGE_VERSION_PATCH = 26;
|
||||
public final static String MAGE_VERSION_MINOR_PATCH = "V8";
|
||||
public final static String MAGE_VERSION_MINOR_PATCH = "V9b";
|
||||
public final static String MAGE_VERSION_INFO = "";
|
||||
|
||||
private final int major;
|
||||
|
|
|
@ -44,6 +44,7 @@ public class DuelCommander extends Commander {
|
|||
banned.add("Eidolon of the Great Revel");
|
||||
banned.add("Emrakul, the Aeons Torn");
|
||||
banned.add("Entomb");
|
||||
banned.add("Fastbond");
|
||||
banned.add("Fireblast");
|
||||
banned.add("Food Chain");
|
||||
banned.add("Gaea's Cradle");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
76
Mage.Sets/src/mage/cards/b/Banshee.java
Normal file
76
Mage.Sets/src/mage/cards/b/Banshee.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
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.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.HalfValue;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DamageControllerEffect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.common.TargetCreatureOrPlayer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class Banshee extends CardImpl {
|
||||
|
||||
public Banshee(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
this.power = new MageInt(0);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// {X}, {T}: Banshee deals half X damage, rounded down, to target creature or player, and half X damage, rounded up, to you.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new HalfValue(new ManacostVariableValue(), false)).setText("Banshee deals half X damage, rounded down, to target creature or player,"), new ManaCostsImpl("{X}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addEffect(new DamageControllerEffect(new HalfValue(new ManacostVariableValue(), true)).setText(" and half X damage, rounded up, to you"));
|
||||
ability.addTarget(new TargetCreatureOrPlayer());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public Banshee(final Banshee card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Banshee copy() {
|
||||
return new Banshee(this);
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ import mage.abilities.effects.common.DynamicManaEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -43,11 +43,10 @@ import mage.filter.common.FilterControlledCreaturePermanent;
|
|||
public class BattleHymn extends CardImpl {
|
||||
|
||||
public BattleHymn(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}");
|
||||
|
||||
// Add {R} to your mana pool for each creature you control.
|
||||
this.getSpellAbility().addEffect(new DynamicManaEffect(Mana.RedMana(1), new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent())));
|
||||
this.getSpellAbility().addEffect(new DynamicManaEffect(Mana.RedMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE)));
|
||||
}
|
||||
|
||||
public BattleHymn(final BattleHymn card) {
|
||||
|
|
110
Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java
Normal file
110
Mage.Sets/src/mage/cards/b/BloodOfTheMartyr.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
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.game.Game;
|
||||
import mage.game.events.DamageEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class BloodOfTheMartyr extends CardImpl {
|
||||
|
||||
public BloodOfTheMartyr(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}{W}");
|
||||
|
||||
// Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
|
||||
this.getSpellAbility().addEffect(new BloodOfTheMartyrEffect());
|
||||
}
|
||||
|
||||
public BloodOfTheMartyr(final BloodOfTheMartyr card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BloodOfTheMartyr copy() {
|
||||
return new BloodOfTheMartyr(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BloodOfTheMartyrEffect extends ReplacementEffectImpl {
|
||||
|
||||
public BloodOfTheMartyrEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.RedirectDamage);
|
||||
staticText = "Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead";
|
||||
}
|
||||
|
||||
public BloodOfTheMartyrEffect(final BloodOfTheMartyrEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BloodOfTheMartyrEffect copy() {
|
||||
return new BloodOfTheMartyrEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGE_CREATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
DamageEvent damageEvent = (DamageEvent) event;
|
||||
if (controller != null) {
|
||||
controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
DamageEvent damageEvent = (DamageEvent) event;
|
||||
return controller != null
|
||||
&& controller.chooseUse(outcome, "Would you like to have " + damageEvent.getAmount() + " damage dealt to you instead of " + game.getPermanentOrLKIBattlefield(damageEvent.getTargetId()).getLogName() + "?", source, game);
|
||||
}
|
||||
}
|
72
Mage.Sets/src/mage/cards/e/EternalFlame.java
Normal file
72
Mage.Sets/src/mage/cards/e/EternalFlame.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.e;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.dynamicvalue.common.HalfValue;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.DamageControllerEffect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class EternalFlame extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("Mountains you control");
|
||||
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.MOUNTAIN));
|
||||
}
|
||||
|
||||
public EternalFlame(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}");
|
||||
|
||||
// Eternal Flame deals X damage to target opponent, where X is the number of Mountains you control. It deals half X damage, rounded up, to you.);
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)).setText("{this} deals X damage to target opponent, where X is the number of Mountains you control"));
|
||||
this.getSpellAbility().addEffect(new DamageControllerEffect(new HalfValue(new PermanentsOnBattlefieldCount(filter), true)).setText("It deals half X damage, rounded up, to you"));
|
||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||
}
|
||||
|
||||
public EternalFlame(final EternalFlame card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EternalFlame copy() {
|
||||
return new EternalFlame(this);
|
||||
}
|
||||
}
|
125
Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java
Normal file
125
Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class GhaltaPrimalHunger extends CardImpl {
|
||||
|
||||
public GhaltaPrimalHunger(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{10}{G}{G}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ELDER);
|
||||
this.subtype.add(SubType.DINOSAUR);
|
||||
this.power = new MageInt(12);
|
||||
this.toughness = new MageInt(12);
|
||||
|
||||
// Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.STACK, new GhaltaPrimalHungerCostReductionEffect()));
|
||||
// Trample
|
||||
this.addAbility(TrampleAbility.getInstance());
|
||||
|
||||
}
|
||||
|
||||
public GhaltaPrimalHunger(final GhaltaPrimalHunger card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GhaltaPrimalHunger copy() {
|
||||
return new GhaltaPrimalHunger(this);
|
||||
}
|
||||
}
|
||||
|
||||
class GhaltaPrimalHungerCostReductionEffect extends CostModificationEffectImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledArtifactPermanent("noncreature artifacts you control");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.not(new CardTypePredicate(CardType.CREATURE)));
|
||||
}
|
||||
|
||||
GhaltaPrimalHungerCostReductionEffect() {
|
||||
super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
staticText = "{this} costs {X} less to cast, where X is the total power of creatures you control";
|
||||
}
|
||||
|
||||
GhaltaPrimalHungerCostReductionEffect(final GhaltaPrimalHungerCostReductionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
int totalPower = 0;
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) {
|
||||
if (permanent.isCreature()) {
|
||||
totalPower += permanent.getPower().getValue();
|
||||
}
|
||||
|
||||
}
|
||||
CardUtil.reduceCost(abilityToModify, totalPower);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
return abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GhaltaPrimalHungerCostReductionEffect copy() {
|
||||
return new GhaltaPrimalHungerCostReductionEffect(this);
|
||||
}
|
||||
}
|
134
Mage.Sets/src/mage/cards/g/GoblinRockSled.java
Normal file
134
Mage.Sets/src/mage/cards/g/GoblinRockSled.java
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.common.combat.CantAttackUnlessDefenderControllsPermanent;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.AttackedLastTurnWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class GoblinRockSled extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent("a Mountain");
|
||||
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.MOUNTAIN));
|
||||
}
|
||||
|
||||
public GoblinRockSled(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}");
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Goblin Rock Sled doesn't untap during your untap step if it attacked during your last turn.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapIfAttackedLastTurnSourceEffect()), new AttackedLastTurnWatcher());
|
||||
|
||||
// Goblin Rock Sled can't attack unless defending player controls a Mountain.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(filter)));
|
||||
}
|
||||
|
||||
public GoblinRockSled(final GoblinRockSled card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoblinRockSled copy() {
|
||||
return new GoblinRockSled(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DontUntapIfAttackedLastTurnSourceEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
public DontUntapIfAttackedLastTurnSourceEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment, false, true);
|
||||
staticText = "{this} doesn't untap during your untap step if it attacked during your last turn";
|
||||
}
|
||||
|
||||
public DontUntapIfAttackedLastTurnSourceEffect(final DontUntapIfAttackedLastTurnSourceEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DontUntapIfAttackedLastTurnSourceEffect copy() {
|
||||
return new DontUntapIfAttackedLastTurnSourceEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.UNTAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (game.getTurn().getStepType() == PhaseStep.UNTAP
|
||||
&& event.getTargetId().equals(source.getSourceId())) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null && permanent.getControllerId().equals(game.getActivePlayerId())) {
|
||||
AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<MageObjectReference> attackingCreatures = watcher.getAttackedLastTurnCreatures(permanent.getControllerId());
|
||||
MageObjectReference mor = new MageObjectReference(permanent, game);
|
||||
if (attackingCreatures.contains(mor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -45,7 +45,7 @@ import mage.constants.SuperType;
|
|||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ public class GrowingRitesOfItlimoc extends CardImpl {
|
|||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new ConditionalTriggeredAbility(
|
||||
new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false),
|
||||
new PermanentsOnTheBattlefieldCondition(new FilterControlledCreaturePermanent(), ComparisonType.MORE_THAN, 3),
|
||||
new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_A_CREATURE, ComparisonType.MORE_THAN, 3),
|
||||
"At the beginning of your end step, if you control four or more creatures, transform {this}"));
|
||||
}
|
||||
|
||||
|
|
|
@ -95,8 +95,11 @@ class InvasionPlansEffect extends ContinuousRuleModifyingEffectImpl {
|
|||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Player blockController = game.getPlayer(game.getCombat().getAttackingPlayerId());
|
||||
if (blockController != null) {
|
||||
game.getCombat().selectBlockers(blockController, game);
|
||||
return event.getPlayerId().equals(game.getCombat().getAttackingPlayerId());
|
||||
// temporary workaround for AI bugging out while choosing blockers
|
||||
if (blockController.isHuman()) {
|
||||
game.getCombat().selectBlockers(blockController, game);
|
||||
return event.getPlayerId().equals(game.getCombat().getAttackingPlayerId());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterOwnedCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
@ -62,6 +64,13 @@ public class IzzetChemister extends CardImpl {
|
|||
|
||||
private static final FilterCard filter = new FilterOwnedCard("instant or sorcery card from your graveyard");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(
|
||||
new CardTypePredicate(CardType.INSTANT),
|
||||
new CardTypePredicate(CardType.SORCERY))
|
||||
);
|
||||
}
|
||||
|
||||
public IzzetChemister(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
|
||||
|
||||
|
|
|
@ -46,14 +46,12 @@ import mage.constants.Layer;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.other.CounterCardPredicate;
|
||||
import mage.filter.predicate.other.OwnerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
@ -149,7 +147,6 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
|
|||
|
||||
static {
|
||||
filter.add(new CounterCardPredicate(CounterType.CAGE));
|
||||
filter.add(new OwnerPredicate(TargetController.YOU));
|
||||
}
|
||||
|
||||
public MairsilThePretenderGainAbilitiesEffect() {
|
||||
|
@ -164,21 +161,21 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent perm = game.getPermanent(source.getSourceId());
|
||||
if (perm != null) {
|
||||
for (Card card : game.getExile().getAllCards(game)) {
|
||||
if (filter.match(card, game)) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
ActivatedAbilityImpl copyAbility = (ActivatedAbilityImpl) ability.copy();
|
||||
copyAbility.setMaxActivationsPerTurn(1);
|
||||
perm.addAbility(copyAbility, card.getId(), game);
|
||||
}
|
||||
if (perm == null) {
|
||||
return false;
|
||||
}
|
||||
for (Card card : game.getExile().getAllCards(game)) {
|
||||
if (filter.match(card, game) && card.getOwnerId() == perm.getControllerId()) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
ActivatedAbilityImpl copyAbility = (ActivatedAbilityImpl) ability.copy();
|
||||
copyAbility.setMaxActivationsPerTurn(1);
|
||||
perm.addAbility(copyAbility, card.getId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,19 +32,14 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
|
@ -54,11 +49,10 @@ import mage.game.permanent.Permanent;
|
|||
public class NaturesWill extends CardImpl {
|
||||
|
||||
public NaturesWill(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{G}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}");
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, tap all lands that player controls and untap all lands you control.
|
||||
this.addAbility(new NaturesWillTriggeredAbility());
|
||||
this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(new NaturesWillEffect()));
|
||||
}
|
||||
|
||||
public NaturesWill(final NaturesWill card) {
|
||||
|
@ -71,63 +65,6 @@ public class NaturesWill extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class NaturesWillTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private boolean madeDamge = false;
|
||||
private Set<UUID> damagedPlayers = new HashSet<>();
|
||||
|
||||
public NaturesWillTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new NaturesWillEffect(), false);
|
||||
}
|
||||
|
||||
public NaturesWillTriggeredAbility(final NaturesWillTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.madeDamge = ability.madeDamge;
|
||||
this.damagedPlayers = new HashSet<>();
|
||||
this.damagedPlayers.addAll(ability.damagedPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturesWillTriggeredAbility copy() {
|
||||
return new NaturesWillTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.DAMAGED_PLAYER || event.getType() == EventType.COMBAT_DAMAGE_STEP_POST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType() == EventType.DAMAGED_PLAYER) {
|
||||
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
|
||||
Permanent p = game.getPermanent(event.getSourceId());
|
||||
if (damageEvent.isCombatDamage() && p != null && p.getControllerId().equals(this.getControllerId())) {
|
||||
madeDamge = true;
|
||||
damagedPlayers.add(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
if (event.getType() == EventType.COMBAT_DAMAGE_STEP_POST) {
|
||||
if (madeDamge) {
|
||||
Set<UUID> damagedPlayersCopy = new HashSet<>();
|
||||
damagedPlayersCopy.addAll(damagedPlayers);
|
||||
for(Effect effect: this.getEffects()) {
|
||||
effect.setValue("damagedPlayers", damagedPlayersCopy);
|
||||
}
|
||||
damagedPlayers.clear();
|
||||
madeDamge = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures you control deal combat damage to a player, " + super.getRule();
|
||||
}
|
||||
}
|
||||
|
||||
class NaturesWillEffect extends OneShotEffect {
|
||||
|
||||
public NaturesWillEffect() {
|
||||
|
@ -160,7 +97,6 @@ class NaturesWillEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,11 +27,9 @@
|
|||
*/
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.ExileFromGraveCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
|
@ -42,10 +40,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
/**
|
||||
|
@ -58,7 +52,7 @@ public class OngoingInvestigation extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, investigate.
|
||||
this.addAbility(new OngoingInvestigationTriggeredAbility());
|
||||
this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(new InvestigateEffect()));
|
||||
|
||||
// {1}{G}, Exile a creature card from your graveyard: Investigate. You gain 2 life.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new InvestigateEffect(), new ManaCostsImpl("{1}{G}"));
|
||||
|
@ -76,50 +70,3 @@ public class OngoingInvestigation extends CardImpl {
|
|||
return new OngoingInvestigation(this);
|
||||
}
|
||||
}
|
||||
|
||||
class OngoingInvestigationTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
List<UUID> damagedPlayerIds = new ArrayList<>();
|
||||
|
||||
public OngoingInvestigationTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new InvestigateEffect(), false);
|
||||
}
|
||||
|
||||
public OngoingInvestigationTriggeredAbility(final OngoingInvestigationTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OngoingInvestigationTriggeredAbility copy() {
|
||||
return new OngoingInvestigationTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|
||||
|| event.getType() == GameEvent.EventType.END_COMBAT_STEP_POST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
|
||||
if (((DamagedPlayerEvent) event).isCombatDamage()) {
|
||||
Permanent creature = game.getPermanent(event.getSourceId());
|
||||
if (creature != null && creature.getControllerId().equals(controllerId)
|
||||
&& !damagedPlayerIds.contains(event.getTargetId())) {
|
||||
damagedPlayerIds.add(event.getTargetId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.getType() == GameEvent.EventType.END_COMBAT_STEP_POST) {
|
||||
damagedPlayerIds.clear();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures you control deal combat damage to a player, investigate";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,11 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.ReturnToHandSourceEffect;
|
||||
|
@ -41,22 +38,14 @@ import mage.abilities.keyword.BloodrushAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
||||
|
||||
public class PyrewildShaman extends CardImpl {
|
||||
|
||||
public PyrewildShaman (UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}");
|
||||
public PyrewildShaman(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.subtype.add(SubType.SHAMAN);
|
||||
|
||||
|
@ -64,14 +53,16 @@ public class PyrewildShaman extends CardImpl {
|
|||
this.toughness = new MageInt(1);
|
||||
|
||||
// Bloodrush — {1}{R}, Discard Pyrewild Shaman: Target attacking creature gets +3/+1 until end of turn.
|
||||
this.addAbility(new BloodrushAbility("{1}{R}", new BoostTargetEffect(3,1,Duration.EndOfTurn)));
|
||||
this.addAbility(new BloodrushAbility("{1}{R}", new BoostTargetEffect(3, 1, Duration.EndOfTurn)));
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, if Pyrewild Shaman is in your graveyard, you may pay {3}. If you do, return Pyrewild Shaman to your hand.
|
||||
this.addAbility(new PyrewildShamanTriggeredAbility());
|
||||
this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone.GRAVEYARD,
|
||||
new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{3}"))
|
||||
.setText("if {this} is in your graveyard, you may pay {3}. If you do, return {this} to your hand")));
|
||||
|
||||
}
|
||||
|
||||
public PyrewildShaman (final PyrewildShaman card) {
|
||||
public PyrewildShaman(final PyrewildShaman card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
|
@ -81,56 +72,3 @@ public class PyrewildShaman extends CardImpl {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
class PyrewildShamanTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
List<UUID> damagedPlayerIds = new ArrayList<>();
|
||||
|
||||
public PyrewildShamanTriggeredAbility() {
|
||||
super(Zone.GRAVEYARD, new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{3}")), false);
|
||||
}
|
||||
|
||||
public PyrewildShamanTriggeredAbility(final PyrewildShamanTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyrewildShamanTriggeredAbility copy() {
|
||||
return new PyrewildShamanTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.DAMAGED_PLAYER
|
||||
|| event.getType() == EventType.END_COMBAT_STEP_POST
|
||||
|| event.getType() == EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
|
||||
if (((DamagedEvent) event).isCombatDamage()) {
|
||||
Permanent creature = game.getPermanent(event.getSourceId());
|
||||
if (creature != null && creature.getControllerId().equals(controllerId) && !damagedPlayerIds.contains(event.getTargetId())) {
|
||||
damagedPlayerIds.add(event.getTargetId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.getType() == EventType.END_COMBAT_STEP_POST){
|
||||
damagedPlayerIds.clear();
|
||||
}
|
||||
if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId())){
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
if (zEvent.getFromZone() == Zone.GRAVEYARD) {
|
||||
damagedPlayerIds.clear();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures you control deal combat damage to a player, if {this} is in your graveyard, you may pay {3}. If you do, return {this} to your hand.";
|
||||
}
|
||||
}
|
||||
|
|
82
Mage.Sets/src/mage/cards/s/StormTheVault.java
Normal file
82
Mage.Sets/src/mage/cards/s/StormTheVault.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.ControlledCreaturesDealCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.v.VaultOfCatlacan;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.permanent.token.TreasureToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class StormTheVault extends CardImpl {
|
||||
|
||||
public StormTheVault(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{R}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = VaultOfCatlacan.class;
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."
|
||||
this.addAbility(new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
|
||||
|
||||
// At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new ConditionalTriggeredAbility(
|
||||
new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect(true), TargetController.YOU, false),
|
||||
new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 4),
|
||||
"At the beginning of your end step, if you control five or more artifacts, transform {this}"));
|
||||
|
||||
}
|
||||
|
||||
public StormTheVault(final StormTheVault card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StormTheVault copy() {
|
||||
return new StormTheVault(this);
|
||||
}
|
||||
}
|
150
Mage.Sets/src/mage/cards/t/TangleKelp.java
Normal file
150
Mage.Sets/src/mage/cards/t/TangleKelp.java
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.TapEnchantedEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.watchers.common.AttackedLastTurnWatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class TangleKelp extends CardImpl {
|
||||
|
||||
public TangleKelp(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}");
|
||||
|
||||
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);
|
||||
|
||||
// When Tangle Kelp enters the battlefield, tap enchanted creature.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect()));
|
||||
|
||||
// Enchanted creature doesn't untap during its controller's untap step if it attacked during its controller's last turn.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapIfAttackedLastTurnEnchantedEffect()), new AttackedLastTurnWatcher());
|
||||
}
|
||||
|
||||
public TangleKelp(final TangleKelp card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TangleKelp copy() {
|
||||
return new TangleKelp(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DontUntapIfAttackedLastTurnEnchantedEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
public DontUntapIfAttackedLastTurnEnchantedEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment, false, true);
|
||||
staticText = "Enchanted creature doesn't untap during its controller's untap step if it attacked during its controller's last turn";
|
||||
}
|
||||
|
||||
public DontUntapIfAttackedLastTurnEnchantedEffect(final DontUntapIfAttackedLastTurnEnchantedEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DontUntapIfAttackedLastTurnEnchantedEffect copy() {
|
||||
return new DontUntapIfAttackedLastTurnEnchantedEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInfoMessage(Ability source, GameEvent event, Game game) {
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null) {
|
||||
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (enchanted != null) {
|
||||
return enchanted.getLogName() + " doesn't untap during its controller's untap step (" + enchantment.getLogName() + ')';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.UNTAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (game.getTurn().getStepType() == PhaseStep.UNTAP) {
|
||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null && event.getTargetId().equals(enchantment.getAttachedTo())) {
|
||||
Permanent permanent = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (permanent != null && permanent.getControllerId().equals(game.getActivePlayerId())) {
|
||||
AttackedLastTurnWatcher watcher = (AttackedLastTurnWatcher) game.getState().getWatchers().get(AttackedLastTurnWatcher.class.getSimpleName());
|
||||
if (watcher != null) {
|
||||
Set<MageObjectReference> attackingCreatures = watcher.getAttackedLastTurnCreatures(permanent.getControllerId());
|
||||
MageObjectReference mor = new MageObjectReference(permanent, game);
|
||||
if (attackingCreatures.contains(mor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
82
Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java
Normal file
82
Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.v;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.DynamicManaEffect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.mana.AnyColorManaAbility;
|
||||
import mage.abilities.mana.SimpleManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class VaultOfCatlacan extends CardImpl {
|
||||
|
||||
public VaultOfCatlacan(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.nightCard = true;
|
||||
|
||||
// <i>(Transforms from Storm the Vault.)</i>
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new InfoEffect("<i>(Transforms from Storm the Vault.)</i>"));
|
||||
ability.setRuleAtTheTop(true);
|
||||
this.addAbility(ability);
|
||||
|
||||
// {T}: Add one mana of any color to your mana pool.
|
||||
this.addAbility(new AnyColorManaAbility());
|
||||
|
||||
// {T}: Add {U} to your mana pool for each artifact you control.
|
||||
this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD,
|
||||
new DynamicManaEffect(Mana.BlueMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)),
|
||||
new TapSourceCost()));
|
||||
|
||||
}
|
||||
|
||||
public VaultOfCatlacan(final VaultOfCatlacan card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultOfCatlacan copy() {
|
||||
return new VaultOfCatlacan(this);
|
||||
}
|
||||
}
|
|
@ -65,9 +65,11 @@ public class Chronicles extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ashnod's Transmogrant", 73, Rarity.COMMON, mage.cards.a.AshnodsTransmogrant.class));
|
||||
cards.add(new SetCardInfo("Axelrod Gunnarson", 107, Rarity.RARE, mage.cards.a.AxelrodGunnarson.class));
|
||||
cards.add(new SetCardInfo("Azure Drake", 15, Rarity.UNCOMMON, mage.cards.a.AzureDrake.class));
|
||||
cards.add(new SetCardInfo("Banshee", 1, Rarity.UNCOMMON, mage.cards.b.Banshee.class));
|
||||
cards.add(new SetCardInfo("Barl's Cage", 74, Rarity.RARE, mage.cards.b.BarlsCage.class));
|
||||
cards.add(new SetCardInfo("Beasts of Bogardan", 45, Rarity.UNCOMMON, mage.cards.b.BeastsOfBogardan.class));
|
||||
cards.add(new SetCardInfo("Blood Moon", 46, Rarity.RARE, mage.cards.b.BloodMoon.class));
|
||||
cards.add(new SetCardInfo("Blood of the Martyr", 60, Rarity.UNCOMMON, mage.cards.b.BloodOfTheMartyr.class));
|
||||
cards.add(new SetCardInfo("Bog Rats", 2, Rarity.COMMON, mage.cards.b.BogRats.class));
|
||||
cards.add(new SetCardInfo("Book of Rass", 75, Rarity.RARE, mage.cards.b.BookOfRass.class));
|
||||
cards.add(new SetCardInfo("Boomerang", 16, Rarity.COMMON, mage.cards.b.Boomerang.class));
|
||||
|
|
|
@ -66,6 +66,7 @@ public class MastersEditionIII extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Ashes to Ashes", 58, Rarity.UNCOMMON, mage.cards.a.AshesToAshes.class));
|
||||
cards.add(new SetCardInfo("Astrolabe", 189, Rarity.COMMON, mage.cards.a.Astrolabe.class));
|
||||
cards.add(new SetCardInfo("Axelrod Gunnarson", 143, Rarity.UNCOMMON, mage.cards.a.AxelrodGunnarson.class));
|
||||
cards.add(new SetCardInfo("Banshee", 59, Rarity.UNCOMMON, mage.cards.b.Banshee.class));
|
||||
cards.add(new SetCardInfo("Barktooth Warbeard", 144, Rarity.COMMON, mage.cards.b.BarktoothWarbeard.class));
|
||||
cards.add(new SetCardInfo("Barl's Cage", 190, Rarity.RARE, mage.cards.b.BarlsCage.class));
|
||||
cards.add(new SetCardInfo("Bartel Runeaxe", 145, Rarity.UNCOMMON, mage.cards.b.BartelRuneaxe.class));
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
package mage.sets;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
/**
|
||||
|
@ -53,5 +54,10 @@ public class RivalsOfIxalan extends ExpansionSet {
|
|||
this.numBoosterUncommon = 3;
|
||||
this.numBoosterRare = 1;
|
||||
this.ratioBoosterMythic = 8;
|
||||
|
||||
cards.add(new SetCardInfo("Ghalta, Primal Hunger", 130, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class));
|
||||
cards.add(new SetCardInfo("Silvergill Adept", 53, Rarity.UNCOMMON, mage.cards.s.SilvergillAdept.class));
|
||||
cards.add(new SetCardInfo("Storm the Vault", 173, Rarity.RARE, mage.cards.s.StormTheVault.class));
|
||||
cards.add(new SetCardInfo("Vault of Catlacan", 173, Rarity.RARE, mage.cards.v.VaultOfCatlacan.class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,8 +57,10 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Apprentice Wizard", 20, Rarity.RARE, mage.cards.a.ApprenticeWizard.class));
|
||||
cards.add(new SetCardInfo("Ashes to Ashes", 1, Rarity.COMMON, mage.cards.a.AshesToAshes.class));
|
||||
cards.add(new SetCardInfo("Ball Lightning", 56, Rarity.RARE, mage.cards.b.BallLightning.class));
|
||||
cards.add(new SetCardInfo("Banshee", 2, Rarity.RARE, mage.cards.b.Banshee.class));
|
||||
cards.add(new SetCardInfo("Barl's Cage", 93, Rarity.RARE, mage.cards.b.BarlsCage.class));
|
||||
cards.add(new SetCardInfo("Blood Moon", 57, Rarity.RARE, mage.cards.b.BloodMoon.class));
|
||||
cards.add(new SetCardInfo("Blood of the Martyr", 75, Rarity.RARE, mage.cards.b.BloodOfTheMartyr.class));
|
||||
cards.add(new SetCardInfo("Bog Imp", 3, Rarity.COMMON, mage.cards.b.BogImp.class));
|
||||
cards.add(new SetCardInfo("Bog Rats", 4, Rarity.COMMON, mage.cards.b.BogRats.class));
|
||||
cards.add(new SetCardInfo("Bone Flute", 94, Rarity.UNCOMMON, mage.cards.b.BoneFlute.class));
|
||||
|
@ -80,6 +82,7 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Eater of the Dead", 6, Rarity.UNCOMMON, mage.cards.e.EaterOfTheDead.class));
|
||||
cards.add(new SetCardInfo("Electric Eel", 24, Rarity.UNCOMMON, mage.cards.e.ElectricEel.class));
|
||||
cards.add(new SetCardInfo("Elves of Deep Shadow", 39, Rarity.UNCOMMON, mage.cards.e.ElvesOfDeepShadow.class));
|
||||
cards.add(new SetCardInfo("Eternal Flame", 60, Rarity.RARE, mage.cards.e.EternalFlame.class));
|
||||
cards.add(new SetCardInfo("Exorcist", 79, Rarity.RARE, mage.cards.e.Exorcist.class));
|
||||
cards.add(new SetCardInfo("Fellwar Stone", 99, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class));
|
||||
cards.add(new SetCardInfo("Festival", 81, Rarity.COMMON, mage.cards.f.Festival.class));
|
||||
|
@ -92,8 +95,10 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Goblin Caves", 63, Rarity.COMMON, mage.cards.g.GoblinCaves.class));
|
||||
cards.add(new SetCardInfo("Goblin Digging Team", 64, Rarity.COMMON, mage.cards.g.GoblinDiggingTeam.class));
|
||||
cards.add(new SetCardInfo("Goblin Hero", 65, Rarity.COMMON, mage.cards.g.GoblinHero.class));
|
||||
cards.add(new SetCardInfo("Goblins of the Flarg", 69, Rarity.COMMON, mage.cards.g.GoblinsOfTheFlarg.class));
|
||||
cards.add(new SetCardInfo("Goblin Rock Sled", 66, Rarity.COMMON, mage.cards.g.GoblinRockSled.class));
|
||||
cards.add(new SetCardInfo("Goblin Shrine", 67, Rarity.COMMON, mage.cards.g.GoblinShrine.class));
|
||||
cards.add(new SetCardInfo("Goblin Wizard", 68, Rarity.RARE, mage.cards.g.GoblinWizard.class));
|
||||
cards.add(new SetCardInfo("Goblins of the Flarg", 69, Rarity.COMMON, mage.cards.g.GoblinsOfTheFlarg.class));
|
||||
cards.add(new SetCardInfo("Grave Robbers", 8, Rarity.RARE, mage.cards.g.GraveRobbers.class));
|
||||
cards.add(new SetCardInfo("Hidden Path", 41, Rarity.RARE, mage.cards.h.HiddenPath.class));
|
||||
cards.add(new SetCardInfo("Holy Light", 83, Rarity.COMMON, mage.cards.h.HolyLight.class));
|
||||
|
@ -133,6 +138,7 @@ public class TheDark extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Standing Stones", 107, Rarity.UNCOMMON, mage.cards.s.StandingStones.class));
|
||||
cards.add(new SetCardInfo("Stone Calendar", 108, Rarity.RARE, mage.cards.s.StoneCalendar.class));
|
||||
cards.add(new SetCardInfo("Sunken City", 35, Rarity.COMMON, mage.cards.s.SunkenCity.class));
|
||||
cards.add(new SetCardInfo("Tangle Kelp", 36, Rarity.RARE, mage.cards.t.TangleKelp.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));
|
||||
|
|
|
@ -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.abilities.common;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ControlledCreaturesDealCombatDamagePlayerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private boolean madeDamage = false;
|
||||
private Set<UUID> damagedPlayerIds = new HashSet<>();
|
||||
|
||||
public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Effect effect) {
|
||||
this(Zone.BATTLEFIELD, effect);
|
||||
}
|
||||
|
||||
public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect) {
|
||||
super(zone, effect, false);
|
||||
}
|
||||
|
||||
public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(final ControlledCreaturesDealCombatDamagePlayerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.madeDamage = ability.madeDamage;
|
||||
this.damagedPlayerIds = new HashSet<>();
|
||||
this.damagedPlayerIds.addAll(ability.damagedPlayerIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlledCreaturesDealCombatDamagePlayerTriggeredAbility copy() {
|
||||
return new ControlledCreaturesDealCombatDamagePlayerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.DAMAGED_PLAYER
|
||||
|| event.getType() == EventType.END_COMBAT_STEP_POST
|
||||
|| event.getType() == EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType() == EventType.DAMAGED_PLAYER) {
|
||||
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
|
||||
Permanent p = game.getPermanent(event.getSourceId());
|
||||
if (damageEvent.isCombatDamage() && p != null && p.getControllerId().equals(this.getControllerId())) {
|
||||
madeDamage = true;
|
||||
damagedPlayerIds.add(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
if (event.getType() == EventType.END_COMBAT_STEP_POST) {
|
||||
if (madeDamage) {
|
||||
Set<UUID> damagedPlayersCopy = new HashSet<>();
|
||||
damagedPlayersCopy.addAll(damagedPlayerIds);
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("damagedPlayers", damagedPlayersCopy);
|
||||
}
|
||||
damagedPlayerIds.clear();
|
||||
madeDamage = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId())) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
if (zEvent.getFromZone() == Zone.GRAVEYARD) {
|
||||
damagedPlayerIds.clear();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures you control deal combat damage to a player, " + super.getRule();
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ public enum CounterType {
|
|||
DIVINITY("divinity"),
|
||||
DOOM("doom"),
|
||||
DREAM("dream"),
|
||||
ECHO("echo"),
|
||||
ELIXIR("elixir"),
|
||||
ENERGY("energy"),
|
||||
EON("eon"),
|
||||
|
|
|
@ -48,6 +48,7 @@ public final class StaticFilters {
|
|||
public static final FilterPermanent FILTER_OPPONENTS_PERMANENT_ARTIFACT = new FilterPermanent("artifact an opponent controls");
|
||||
public static final FilterPermanent FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE = new FilterPermanent("artifact or creature an opponent controls");
|
||||
|
||||
public static final FilterPermanent FILTER_CONTROLLED_CREATURE = new FilterControlledCreaturePermanent();
|
||||
public static final FilterPermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control");
|
||||
public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_ANOTHER_CREATURE = new FilterControlledCreaturePermanent("another creature");
|
||||
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_NON_LAND = new FilterControlledPermanent("nonland permanent");
|
||||
|
|
|
@ -32696,3 +32696,9 @@ Forest|Duel Decks: Mind vs. Might|65|L||Basic Land - Forest|||{T}: Add {G} to yo
|
|||
Chandra, Gremlin Wrangler|Heroes of the Realm|1|M|{2}{R}{R}|Legendary Planeswalker - Chandra|3||+1: Create a 2/2 red Gremlin creature token.$-2:Chandra, Gremlin Wrangler deals X damage to target creature or player, where X is the number of Gremlins you control.|
|
||||
Dungeon Master|Heroes of the Realm|1|M|{2}{W}{U}|Legendary Planeswalker - Dungeon Master|||+1: Target opponent creates a 1/1 black Skeleton creature token with “When this creature dies, each opponent loses 2 life.”$+1: Roll a d20. If you roll a 1, skip your next turn. If you roll a 12 or higher, draw a card.$-6: You get an adventuring party. (Your party is a 3/3 red Fighter with first strike, a 1/1 white Cleric with lifelink, a 2/2 black Rogue with hexproof, and a 1/1 blue Wizard with flying.)|
|
||||
Nira, Hellkite Duelist|Heroes of the Realm|3|M|{W}{U}{B}{R}{G}|Legendary Creature — Dragon|6|6|Flash$Flying, trample, haste$When Nira, Hellkite Duelist enters the battlefield, the next time you would lose the game this turn, instead draw three cards and your life total becomes 5.|
|
||||
Silvergill Adept|Rivals of Ixalan|53|U|{1}{U}|Creature - Merfolk Wizard|2|1|As an additional cost to cast Silvergill Adept, reveal a Merfolk card from your hand or pay {3}.$When Silvergill Adept enters the battlefield, draw a card.|
|
||||
Tetzimoc, Primal Death|Rivals of Ixalan|86|R|{4}{B}{B}|Legendary Creature - Elder Dinosaur|6|6|Deathtouch${B}, Reveal Tetzimoc, Primal Death from your hand: Put a prey counter on target creature. Activate this ability only during your turn.$When Tetzimoc, Primal Death enters the battlefield, destroy each creature your opponents control with a prey counter on it.|
|
||||
Ghalta, Primal Hunger|Rivals of Ixalan|130|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.$Trample|
|
||||
Storm the Vault|Rivals of Ixalan|173|R|{2}{U}{R}|Legendary Enchantment|||Whenever one or more creatures you control deal combat damage to a player, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool."$At the beginning of your end step, if you control five or more artifacts, transform Storm the Vault.|
|
||||
Vault of Catlacan|Rivals of Ixalan|173|R||Legendary Land|||<i>(Transforms from Storm the Vault.)</i>${T}: Add one mana of any color to your mana pool.${T}: Add {U} to your mana pool for each artifact you control.|
|
||||
The Immortal Sun|Rivals of Ixalan|180|M|{6}|Legendary Artifact|||Players can't activate loyalty abilities of planeswalkers.$At the beginning of your draw step, draw an additional card.$Spells you cast cost {1} less to cast.$Creatures you control get +1/+1.|
|
Loading…
Reference in a new issue