Merge branch 'magefree:master' into case-of-the-pilfered-proof

This commit is contained in:
Matthew Wilson 2024-04-07 14:31:44 +03:00 committed by GitHub
commit 9cf6119c7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
797 changed files with 36049 additions and 3584 deletions

View file

@ -151,13 +151,6 @@
<version>1.17</version>
</dependency>
<!-- svg support END -->
<dependency>
<!-- time lib for GUI time info -->
<groupId>org.ocpsoft.prettytime</groupId>
<artifactId>prettytime</artifactId>
<version>4.0.6.Final</version>
</dependency>
</dependencies>
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->

View file

@ -6,6 +6,8 @@ import mage.client.util.GUISizeHelper;
import mage.client.util.MageTableRowSorter;
import mage.client.util.gui.TableUtil;
import mage.client.util.gui.countryBox.CountryCellRenderer;
import mage.components.table.MageTable;
import mage.components.table.TableInfo;
import mage.remote.MageRemoteException;
import mage.view.RoomUsersView;
import mage.view.UsersView;

View file

@ -13,6 +13,9 @@ import mage.client.util.MageTableRowSorter;
import mage.client.util.URLHandler;
import mage.client.util.gui.GuiDisplayUtil;
import mage.client.util.gui.TableUtil;
import mage.components.table.MageTable;
import mage.components.table.TableInfo;
import mage.components.table.TimeAgoTableCellRenderer;
import mage.constants.*;
import mage.game.match.MatchOptions;
import mage.players.PlayerType;
@ -155,19 +158,8 @@ public class TablesPanel extends javax.swing.JPanel {
final JToggleButton[] filterButtons;
// time formater
private final PrettyTime timeFormater = new PrettyTime(Locale.ENGLISH);
// 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;
}
};
// time formatter
private final PrettyTime timeFormatter = new PrettyTime(Locale.ENGLISH);
// duration renderer
TableCellRenderer durationCellRenderer = new DefaultTableCellRenderer() {
@ -177,8 +169,8 @@ public class TablesPanel extends javax.swing.JPanel {
Long ms = (Long) value;
if (ms != 0) {
Duration dur = timeFormater.approximateDuration(new Date(ms));
label.setText((timeFormater.formatDuration(dur)));
Duration dur = timeFormatter.approximateDuration(new Date(ms));
label.setText((timeFormatter.formatDuration(dur)));
} else {
label.setText("");
}
@ -298,13 +290,8 @@ public class TablesPanel extends javax.swing.JPanel {
initComponents();
// tableModel.setSession(session);
// formater
// change default just now from 60 to 30 secs
// see workaround for 4.0 versions: https://github.com/ocpsoft/prettytime/issues/152
TimeFormat timeFormat = timeFormater.removeUnit(JustNow.class);
JustNow newJustNow = new JustNow();
newJustNow.setMaxQuantity(1000L * 30L); // 30 seconds gap (show "just now" from 0 to 30 secs)
timeFormater.registerUnit(newJustNow, timeFormat);
// formatter
MageTable.fixTimeFormatter(this.timeFormatter);
// 1. TABLE CURRENT
tableTables.createDefaultColumnsFromModel();
@ -334,7 +321,7 @@ public class TablesPanel extends javax.swing.JPanel {
tableTables.setRowSorter(activeTablesSorter);
// time ago
tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_CREATED).setCellRenderer(timeAgoCellRenderer);
tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_CREATED).setCellRenderer(TimeAgoTableCellRenderer.getInstance());
// skill level
tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_SKILL).setCellRenderer(skillCellRenderer);
// seats
@ -545,7 +532,7 @@ public class TablesPanel extends javax.swing.JPanel {
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
int modelRow = TablesUtil.getSelectedModelRow(table);
int modelRow = MageTable.getSelectedModelRow(table);
if (modelRow != -1) {
// needs only selected
String rowId = TablesUtil.getSearchIdFromTable(table, modelRow);
@ -563,7 +550,7 @@ public class TablesPanel extends javax.swing.JPanel {
public void run() {
String lastRowID = tablesLastSelection.get(table);
int needModelRow = TablesUtil.findTableRowFromSearchId(table.getModel(), lastRowID);
int needViewRow = TablesUtil.getViewRowFromModel(table, needModelRow);
int needViewRow = MageTable.getViewRowFromModel(table, needModelRow);
if (needViewRow != -1) {
table.clearSelection();
table.addRowSelectionInterval(needViewRow, needViewRow);
@ -581,7 +568,7 @@ public class TablesPanel extends javax.swing.JPanel {
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
int modelRow = TablesUtil.getSelectedModelRow(table);
int modelRow = MageTable.getSelectedModelRow(table);
if (e.getClickCount() == 2 && modelRow != -1) {
action.actionPerformed(new ActionEvent(table, ActionEvent.ACTION_PERFORMED, TablesUtil.getSearchIdFromTable(table, modelRow)));
}

View file

@ -1,6 +1,7 @@
package mage.client.table;
import mage.client.SessionHandler;
import mage.components.table.TableModelWithTooltip;
import mage.constants.SkillLevel;
import mage.remote.MageRemoteException;
import mage.view.TableView;

View file

@ -5,6 +5,8 @@ import org.apache.log4j.Logger;
import javax.swing.*;
/**
* GUI related
*
* @author JayDi85
*/
public class TablesUtil {
@ -42,22 +44,4 @@ public class TablesUtil {
}
return row;
}
public static int getSelectedModelRow(JTable table) {
return getModelRowFromView(table, table.getSelectedRow());
}
public static int getModelRowFromView(JTable table, int viewRow) {
if (viewRow != -1 && viewRow < table.getModel().getRowCount()) {
return table.convertRowIndexToModel(viewRow);
}
return -1;
}
public static int getViewRowFromModel(JTable table, int modelRow) {
if (modelRow != -1 && modelRow < table.getModel().getRowCount()) {
return table.convertRowIndexToView(modelRow);
}
return -1;
}
}

View file

@ -186,13 +186,6 @@ public final class GUISizeHelper {
}
}
public static String textToHtmlWithSize(String text, Font font) {
if (text != null && !text.toLowerCase(Locale.ENGLISH).startsWith("<html>")) {
return "<html><p style=\"font-size: " + font.getSize() + ";\">" + text + "</p>";
}
return text;
}
/**
* Return scrollbar settings, so user can scroll it more faster for bigger cards
*

View file

@ -97,13 +97,14 @@ public class GathererSets implements Iterable<DownloadJob> {
"GNT", "UMA", "GRN",
"RNA", "WAR", "MH1",
"M20",
"C19","ELD","MB1","GN2","J20","THB","UND","C20","IKO","M21",
"JMP","2XM","ZNR","KLR","CMR","KHC","KHM","TSR","STX","STA",
"C21","MH2","AFR","AFC","J21","MID","MIC","VOW","VOC","YMID",
"NEC","NEO","SNC","NCC","CLB","2X2","DMU","DMC","40K","GN3",
"UNF","BRO","BRC","BOT","30A","J22","SCD","DMR","ONE","ONC",
"MOM","MOC","MUL","MAT","LTR","CMM","WOE","WHO","RVR","WOT",
"WOC","SPG","LCI","LCC","REX"
"C19", "ELD", "MB1", "GN2", "J20", "THB", "UND", "C20", "IKO", "M21",
"JMP", "2XM", "ZNR", "KLR", "CMR", "KHC", "KHM", "TSR", "STX", "STA",
"C21", "MH2", "AFR", "AFC", "J21", "MID", "MIC", "VOW", "VOC", "YMID",
"NEC", "NEO", "SNC", "NCC", "CLB", "2X2", "DMU", "DMC", "40K", "GN3",
"UNF", "BRO", "BRC", "BOT", "30A", "J22", "SCD", "DMR", "ONE", "ONC",
"MOM", "MOC", "MUL", "MAT", "LTR", "CMM", "WOE", "WHO", "RVR", "WOT",
"WOC", "SPG", "LCI", "LCC", "REX", "PIP", "MKM", "MKC", "CLU", "OTJ",
"OTC", "OTP", "BIG", "MH3", "ACR", "BLB"
// "HHO", "ANA" -- do not exist on gatherer
};

View file

@ -547,6 +547,8 @@ public class ScryfallImageSupportCards {
add("MKC"); // Murders at Karlov Manor Commander
add("CLU"); // Ravnica: Clue Edition
add("OTJ"); // Outlaws of Thunder Junction
add("OTC"); // Outlaws of Thunder Junction Commander
add("OTP"); // Breaking News
add("BIG"); // The Big Score
add("MH3"); // Modern Horizons 3
add("ACR"); // Assassin's Creed

View file

@ -43,6 +43,13 @@
<version>4.2.2.GA</version>
</dependency>
<dependency>
<!-- time lib for GUI time info -->
<groupId>org.ocpsoft.prettytime</groupId>
<artifactId>prettytime</artifactId>
<version>4.0.6.Final</version>
</dependency>
<dependency>
<!-- A dependency of jboss.remoting -->
<groupId>concurrent</groupId>

View file

@ -1,4 +1,4 @@
package mage.client.table;
package mage.components.table;
/**
* @author JayDi85

View file

@ -1,13 +1,17 @@
package mage.client.table;
package mage.components.table;
import mage.client.util.GUISizeHelper;
import org.apache.log4j.Logger;
import org.ocpsoft.prettytime.PrettyTime;
import org.ocpsoft.prettytime.TimeFormat;
import org.ocpsoft.prettytime.units.JustNow;
import javax.swing.*;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Locale;
/**
* GUI: basic mage table for any data like game tables list, players list, etc
@ -26,7 +30,7 @@ public class MageTable extends JTable {
public MageTable(TableInfo tableInfo) {
this.tableInfo = tableInfo;
}
public void setTableInfo(TableInfo tableInfo) {
this.tableInfo = tableInfo;
}
@ -37,7 +41,7 @@ public class MageTable extends JTable {
java.awt.Point p = e.getPoint();
int viewRow = rowAtPoint(p);
int viewCol = columnAtPoint(p);
int modelRow = TablesUtil.getModelRowFromView(this, viewRow);
int modelRow = getModelRowFromView(this, viewRow);
int modelCol = this.convertColumnIndexToModel(viewCol);
String tip = null;
if (modelRow != -1 && modelCol != -1) {
@ -48,7 +52,7 @@ public class MageTable extends JTable {
tip = model.getValueAt(modelRow, modelCol).toString();
}
}
return GUISizeHelper.textToHtmlWithSize(tip, GUISizeHelper.tableFont);
return textToHtmlWithSize(tip, this.getFont());
}
@Override
@ -78,8 +82,43 @@ public class MageTable extends JTable {
tip = col.getHeaderValue().toString();
}
return GUISizeHelper.textToHtmlWithSize(tip, GUISizeHelper.tableFont);
return textToHtmlWithSize(tip, MageTable.this.getFont());
}
};
}
public static int getSelectedModelRow(JTable table) {
return getModelRowFromView(table, table.getSelectedRow());
}
public static int getModelRowFromView(JTable table, int viewRow) {
if (viewRow != -1 && viewRow < table.getModel().getRowCount()) {
return table.convertRowIndexToModel(viewRow);
}
return -1;
}
public static int getViewRowFromModel(JTable table, int modelRow) {
if (modelRow != -1 && modelRow < table.getModel().getRowCount()) {
return table.convertRowIndexToView(modelRow);
}
return -1;
}
public static String textToHtmlWithSize(String text, Font font) {
if (text != null && !text.toLowerCase(Locale.ENGLISH).startsWith("<html>")) {
return "<html><p style=\"font-size: " + font.getSize() + ";\">" + text + "</p>";
}
return text;
}
public static void fixTimeFormatter(PrettyTime timeFormatter) {
// TODO: remove after PrettyTime lib upgrade to v5
// change default just now from 60 to 30 secs
// see workaround for 4.0 versions: https://github.com/ocpsoft/prettytime/issues/152
TimeFormat timeFormat = timeFormatter.removeUnit(JustNow.class);
JustNow newJustNow = new JustNow();
newJustNow.setMaxQuantity(1000L * 30L); // 30 seconds gap (show "just now" from 0 to 30 secs)
timeFormatter.registerUnit(newJustNow, timeFormat);
}
}

View file

@ -1,4 +1,4 @@
package mage.client.table;
package mage.components.table;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,7 +1,9 @@
package mage.client.table;
package mage.components.table;
/**
* GUI: add support of tooltip/hint for table's cells on mouse move (used by MageTable)
* <p>
* Make sure form and java files uses new MageTable(), not new JTable() code
*
* @author JayDi85
*/

View file

@ -0,0 +1,41 @@
package mage.components.table;
import org.ocpsoft.prettytime.PrettyTime;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.util.Date;
import java.util.Locale;
/**
* GUI: create time ago cell renderer for date values in the table's cell
* <p>
* Usage example:
* tableTables.getColumnModel().getColumn(TablesTableModel.COLUMN_CREATED).setCellRenderer(TimeAgoTableCellRenderer.getInstance());
*
* @author JayDi85
*/
public class TimeAgoTableCellRenderer extends DefaultTableCellRenderer {
static final PrettyTime timeFormatter;
static {
timeFormatter = new PrettyTime(Locale.ENGLISH);
MageTable.fixTimeFormatter(timeFormatter);
}
private static final TimeAgoTableCellRenderer instance = new TimeAgoTableCellRenderer();
public static TimeAgoTableCellRenderer getInstance() {
return instance;
}
@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(timeFormatter.format(d));
return label;
}
}

View file

@ -32,6 +32,7 @@ public class ConnectDialog extends JDialog {
public ConnectDialog() {
initComponents();
cbProxyType.setModel(new DefaultComboBoxModel(Connection.ProxyType.values()));
setVisible(false);
}
public void showDialog(ConsoleFrame console) {
@ -39,8 +40,8 @@ public class ConnectDialog extends JDialog {
this.txtServer.setText(ConsoleFrame.getPreferences().get("serverAddress", "localhost"));
this.txtPort.setText(ConsoleFrame.getPreferences().get("serverPort", Integer.toString(17171)));
this.chkAutoConnect.setSelected(Boolean.parseBoolean(ConsoleFrame.getPreferences().get("autoConnect", "false")));
this.txtProxyServer.setText(ConsoleFrame.getPreferences().get("proxyAddress", "localhost"));
this.txtProxyPort.setText(ConsoleFrame.getPreferences().get("proxyPort", Integer.toString(17171)));
this.txtProxyServer.setText(ConsoleFrame.getPreferences().get("proxyAddress", ""));
this.txtProxyPort.setText(ConsoleFrame.getPreferences().get("proxyPort", Integer.toString(0)));
this.cbProxyType.setSelectedItem(Connection.ProxyType.valueOf(ConsoleFrame.getPreferences().get("proxyType", "NONE").toUpperCase(Locale.ENGLISH)));
this.txtProxyUserName.setText(ConsoleFrame.getPreferences().get("proxyUsername", ""));
this.txtPasswordField.setText(ConsoleFrame.getPreferences().get("proxyPassword", ""));
@ -71,7 +72,16 @@ public class ConnectDialog extends JDialog {
private void saveSettings() {
ConsoleFrame.getPreferences().put("serverAddress", txtServer.getText());
ConsoleFrame.getPreferences().put("serverPort", txtPort.getText());
ConsoleFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected()));
if (chkAutoConnect.isSelected()) {
ConsoleFrame.getPreferences().putBoolean("autoConnect", true);
char[] input = txtPassword.getPassword();
ConsoleFrame.getPreferences().put("password", new String(input));
Arrays.fill(input, '0');
} else {
ConsoleFrame.getPreferences().putBoolean("autoConnect", false);
ConsoleFrame.getPreferences().put("password", "");
}
ConsoleFrame.getPreferences().put("proxyAddress", txtProxyServer.getText());
ConsoleFrame.getPreferences().put("proxyPort", txtProxyPort.getText());
ConsoleFrame.getPreferences().put("proxyType", cbProxyType.getSelectedItem().toString());

View file

@ -11,6 +11,7 @@ import org.apache.log4j.Logger;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -63,6 +64,13 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
session = new SessionImpl(this);
connectDialog = new ConnectDialog();
// try auto connect
if (!autoConnect()) {
SwingUtilities.invokeLater(() -> {
connectDialog.showDialog(this);
});
}
} catch (Exception ex) {
logger.fatal("", ex);
}
@ -78,6 +86,29 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
return false;
}
public boolean autoConnect() {
boolean needAutoConnect = Boolean.parseBoolean(ConsoleFrame.getPreferences().get("autoConnect", "false"));
boolean status = false;
if (needAutoConnect) {
String server = ConsoleFrame.getPreferences().get("serverAddress", "localhost");
logger.info("Auto-connecting to " + server);
Connection newConnection = new Connection();
newConnection.setHost(server);
newConnection.setPort(ConsoleFrame.getPreferences().getInt("serverPort", 17171));
newConnection.setUsername(SessionImpl.ADMIN_NAME);
newConnection.setAdminPassword(ConsoleFrame.getPreferences().get("password", ""));
newConnection.setProxyType(Connection.ProxyType.valueOf(ConsoleFrame.getPreferences().get("proxyType", "NONE").toUpperCase(Locale.ENGLISH)));
if (!newConnection.getProxyType().equals(Connection.ProxyType.NONE)) {
newConnection.setProxyHost(ConsoleFrame.getPreferences().get("proxyAddress", ""));
newConnection.setProxyPort(ConsoleFrame.getPreferences().getInt("proxyPort", 0));
newConnection.setProxyUsername(ConsoleFrame.getPreferences().get("proxyUsername", ""));
newConnection.setProxyPassword(ConsoleFrame.getPreferences().get("proxyPassword", ""));
}
status = connect(newConnection);
}
return status;
}
public void setStatusText(String status) {
this.lblStatus.setText(status);
}

View file

@ -88,6 +88,9 @@
<Connection code="tableUserModel" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MageTable()"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
@ -262,6 +265,9 @@
<Connection code="tableTableModel" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MageTable()"/>
</AuxValues>
</Component>
</SubComponents>
</Container>

View file

@ -1,5 +1,8 @@
package mage.server.console;
import mage.components.table.MageTable;
import mage.components.table.TableModelWithTooltip;
import mage.components.table.TimeAgoTableCellRenderer;
import mage.remote.Session;
import mage.view.TableView;
import mage.view.UserView;
@ -13,6 +16,7 @@
import java.util.*;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
@ -36,6 +40,7 @@
this.tableUserModel = new TableUserModel();
this.tableTableModel = new TableTableModel();
initComponents();
spinnerMuteDurationMinutes.setValue(60);
this.tblUsers.createDefaultColumnsFromModel();
this.tblUsers.setRowSorter(new TableRowSorter(tableUserModel));
@ -43,7 +48,8 @@
this.tblTables.createDefaultColumnsFromModel();
this.tblTables.setRowSorter(new TableRowSorter(tableTableModel));
this.tblUsers.setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
this.tblTables.setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
this.tblTables.getColumnModel().getColumn(TableTableModel.COLUMN_CREATED).setCellRenderer(TimeAgoTableCellRenderer.getInstance());
}
public void update(List<UserView> users) {
@ -93,7 +99,7 @@
jPanel1 = new javax.swing.JPanel();
jPanel3 = new javax.swing.JPanel();
jScrollPane1 = new javax.swing.JScrollPane();
tblUsers = new javax.swing.JTable();
tblUsers = new MageTable();
jPanel4 = new javax.swing.JPanel();
btnDisconnect = new javax.swing.JButton();
btnEndSession = new javax.swing.JButton();
@ -105,7 +111,7 @@
jPanel2 = new javax.swing.JPanel();
jPanel5 = new javax.swing.JPanel();
jScrollPane2 = new javax.swing.JScrollPane();
tblTables = new javax.swing.JTable();
tblTables = new MageTable();
jPanel6 = new javax.swing.JPanel();
btnRemoveTable = new javax.swing.JButton();
jUserName = new javax.swing.JTextField();
@ -373,7 +379,10 @@
private void btnRemoveTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveTableActionPerformed
int row = this.tblTables.convertRowIndexToModel(tblTables.getSelectedRow());
ConsoleFrame.getSession().removeTable((UUID) tableTableModel.getValueAt(row, 7));
if (row >= 0) {
TableView tableView = this.tableTableModel.getTableView(row);
ConsoleFrame.getSession().removeTable(tableView.getTableId());
}
}//GEN-LAST:event_btnRemoveTableActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
@ -484,9 +493,11 @@
}
class TableTableModel extends AbstractTableModel {
class TableTableModel extends AbstractTableModel implements TableModelWithTooltip {
private final String[] columnNames = new String[]{"Table Name", "Owner", "Game Type", "Deck Type", "Status"};
protected static final int COLUMN_CREATED = 4;
private final String[] columnNames = new String[]{"Table name", "Players", "Game type", "Deck type", "Created", "Status", "Games"};
private TableView[] tables = new TableView[0];
public void loadData(Collection<TableView> tables) {
@ -515,17 +526,20 @@
return tables[arg0].getGameType();
case 3:
return tables[arg0].getDeckType();
case 4:
return tables[arg0].getTableState().toString();
case COLUMN_CREATED:
return tables[arg0].getCreateTime();
case 5:
return tables[arg0].isTournament();
case 6:
if (!tables[arg0].getGames().isEmpty()) {
return tables[arg0].getGames().get(0);
return tables[arg0].getTableStateText();
case 6:{
if (tables[arg0].getGames().isEmpty()) {
return "NO GAMES";
} else if (tables[arg0].getGames().size() == 1) {
return tables[arg0].getGames().get(0).toString();
} else {
return String.format("%d games:", tables[arg0].getGames().size())
+ "<br>" + tables[arg0].getGames().stream().map(UUID::toString).collect(Collectors.joining("<br>"));
}
return null;
case 7:
return tables[arg0].getTableId();
}
}
return "";
}
@ -543,14 +557,31 @@
@Override
public Class getColumnClass(int columnIndex) {
return String.class;
if (columnIndex == COLUMN_CREATED) {
return Date.class;
} else {
return String.class;
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 5;
return false;
}
public TableView getTableView(int row) {
if (row >= 0 && row <= this.tables.length - 1) {
return this.tables[row];
} else {
throw new IllegalArgumentException("Unknown table row: " + row);
}
}
@Override
public String getTooltipAt(int rowIndex, int columnIndex) {
Object res = this.getValueAt(rowIndex, columnIndex);
return res == null ? null : res.toString();
}
}
class UpdateUsersTask extends SwingWorker<Void, List<UserView>> {

View file

@ -449,8 +449,10 @@ public class Session {
logger.warn("SESSION LOCK, possible connection problem - fireCallback - userId: " + userId + " messageId: " + call.getMessageId(), ex);
}
} catch (HandleCallbackException ex) {
// something wrong, maybe connection problem
logger.warn("SESSION CALLBACK EXCEPTION - " + ThreadUtils.findRootException(ex) + ", userId " + userId + ", messageId: " + call.getMessageId(), ex);
// general error
// can raise on server freeze or normal connection problem from a client side
// no need to print a full stack log here
logger.warn("SESSION CALLBACK EXCEPTION - " + ThreadUtils.findRootException(ex) + ", userId " + userId + ", messageId: " + call.getMessageId());
// do not send data anymore (user must reconnect)
this.valid = false;

View file

@ -0,0 +1,48 @@
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.mana.RedManaAbility;
import mage.abilities.mana.WhiteManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AbradedBluffs extends CardImpl {
public AbradedBluffs(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.subtype.add(SubType.DESERT);
// Abraded Bluffs enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility());
// When Abraded Bluffs enters the battlefield, it deals 1 damage to target opponent.
Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "it"));
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
// {T}: Add {R} or {W}.
this.addAbility(new RedManaAbility());
this.addAbility(new WhiteManaAbility());
}
private AbradedBluffs(final AbradedBluffs card) {
super(card);
}
@Override
public AbradedBluffs copy() {
return new AbradedBluffs(this);
}
}

View file

@ -9,6 +9,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.DamagedBatchForOnePermanentEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
@ -56,14 +57,22 @@ class AegarTheFreezingFlameTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
DamagedEvent dEvent = (DamagedEvent) event;
if (dEvent.getExcess() < 1
|| !game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId()))) {
DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event;
int excess = dEvent.getEvents()
.stream()
.mapToInt(DamagedEvent::getExcess)
.sum();
boolean controlledByOpponent =
game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId()));
if (excess < 1 || !controlledByOpponent) {
return false;
}
AegarTheFreezingFlameWatcher watcher = game.getState().getWatcher(AegarTheFreezingFlameWatcher.class);

View file

@ -63,7 +63,7 @@ class AetherSnapEffect extends OneShotEffect {
return false;
}
Cards tokens = new CardsImpl();
for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getSourceId(), game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
if (permanent instanceof PermanentToken) {
tokens.add(permanent);
}

View file

@ -60,7 +60,7 @@ class AetherworksMarvelEffect extends OneShotEffect {
AetherworksMarvelEffect() {
super(Outcome.PlayForFree);
this.staticText = "Look at the top six cards of your library. "
+ "You may cast a card from among them without paying "
+ "You may cast a spell from among them without paying "
+ "its mana cost. Put the rest on the bottom of your "
+ "library in a random order";
}

View file

@ -0,0 +1,135 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.*;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardAndOrCard;
import mage.target.common.TargetCardAndOrCardInLibrary;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author notgreat
*/
public final class AgencyOutfitter extends CardImpl {
public AgencyOutfitter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
this.subtype.add(SubType.SPHINX);
this.subtype.add(SubType.DETECTIVE);
this.power = new MageInt(4);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Agency Outfitter enters the battlefield, you may search your graveyard, hand, and/or library for a card named Magnifying Glass and/or a card named Thinking Cap and put them onto the battlefield. If you search your library this way, shuffle.
this.addAbility(new EntersBattlefieldTriggeredAbility(new AgencyOutfitterEffect(), true));
}
private AgencyOutfitter(final AgencyOutfitter card) {
super(card);
}
@Override
public AgencyOutfitter copy() {
return new AgencyOutfitter(this);
}
}
class AgencyOutfitterEffect extends OneShotEffect {
private static final String glassName = "Magnifying Glass";
private static final String capName = "Thinking Cap";
AgencyOutfitterEffect() {
super(Outcome.UnboostCreature);
this.staticText = "you may search your graveyard, hand, and/or library for a card named Magnifying Glass and/or a card named Thinking Cap and put them onto the battlefield. If you search your library this way, shuffle.";
}
private AgencyOutfitterEffect(final AgencyOutfitterEffect effect) {
super(effect);
}
@Override
public AgencyOutfitterEffect copy() {
return new AgencyOutfitterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Card glassCard = null;
Card capCard = null;
if (controller.chooseUse(Outcome.Neutral, "Search your library?", source, game)) {
TargetCardAndOrCardInLibrary libraryTarget = new TargetCardAndOrCardInLibrary(glassName, capName);
if (controller.searchLibrary(libraryTarget, source, game)) {
for (UUID id : libraryTarget.getTargets()) {
Card card = game.getCard(id);
if (card != null) {
if (CardUtil.haveSameNames(card, glassName, game)) {
glassCard = card;
} else if (CardUtil.haveSameNames(card, capName, game)) {
capCard = card;
}
}
}
}
controller.shuffleLibrary(source, game);
}
if (glassCard == null || capCard == null) {
FilterCard filter;
TargetCard target;
if (glassCard == null && capCard == null) {
target = new TargetCardAndOrCard(glassName, capName);
filter = target.getFilter();
} else {
String name = (glassCard == null ? glassName : capName);
filter = new FilterCard();
filter.add(new NamePredicate(name));
target = new TargetCard(0, 1, Zone.ALL, filter);
}
target.withNotTarget(true);
Cards cards = new CardsImpl();
cards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game));
cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game));
if (!cards.isEmpty()) {
controller.choose(outcome, cards, target, source, game);
for (UUID id : target.getTargets()) {
Card card = game.getCard(id);
if (card != null) {
if (CardUtil.haveSameNames(card, glassName, game)) {
glassCard = card;
} else if (CardUtil.haveSameNames(card, capName, game)) {
capCard = card;
}
}
}
}
}
Cards foundCards = new CardsImpl();
foundCards.add(glassCard);
foundCards.add(capCard);
if (!foundCards.isEmpty()) {
controller.moveCards(foundCards, Zone.BATTLEFIELD, source, game);
}
return true;
}
}

View file

@ -94,7 +94,7 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl {
if (targetingObject == null || targetingObject instanceof Spell) {
return false;
}
if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) {
if (CardUtil.checkTargetedEventAlreadyUsed(this.getId().toString(), targetingObject, event, game)) {
return false;
}
Set<UUID> targets = targetingObject

View file

@ -0,0 +1,192 @@
package mage.cards.a;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.dynamicvalue.common.CreaturesYouControlCount;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.CatWarrior21Token;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetAnyTarget;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Susucr
*/
public final class AjaniNacatlAvenger extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(SubType.CAT, "Cat you control");
public AjaniNacatlAvenger(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.setStartingLoyalty(3);
this.color.setRed(true);
this.color.setWhite(true);
this.nightCard = true;
// +2: Put a +1/+1 counter on each Cat you control.
this.addAbility(new LoyaltyAbility(
new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), 2
));
// 0: Create a 2/1 white Car Warrior creature token. When you do, if you control a red permanent other than Ajani, Nacatl Avenger, he deals damage equal to the number of creatures you control to any target.
this.addAbility(new LoyaltyAbility(new AjaniNacatlAvengerZeroEffect(), 0));
// -4: Each opponent chooses an artifact, a creature, an enchantment and a planeswalker from among the nonland permanents they control, then sacrifices the rest.
this.addAbility(new LoyaltyAbility(new AjaniNacatlAvengerMinusFourEffect(), -4));
}
private AjaniNacatlAvenger(final AjaniNacatlAvenger card) {
super(card);
}
@Override
public AjaniNacatlAvenger copy() {
return new AjaniNacatlAvenger(this);
}
}
class AjaniNacatlAvengerZeroEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("red permanent other than {this}");
static {
filter.add(new ColorPredicate(ObjectColor.RED));
filter.add(AnotherPredicate.instance);
}
private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, true);
AjaniNacatlAvengerZeroEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "Create a 2/1 white Car Warrior creature token. "
+ "When you do, if you control a red permanent other than {this}, "
+ "he deals damage equal to the number of creatures you control to any target.";
}
private AjaniNacatlAvengerZeroEffect(final AjaniNacatlAvengerZeroEffect effect) {
super(effect);
}
@Override
public AjaniNacatlAvengerZeroEffect copy() {
return new AjaniNacatlAvengerZeroEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
if (!new CreateTokenEffect(new CatWarrior21Token()).apply(game, source)) {
return false;
}
ReflexiveTriggeredAbility reflexive = new ReflexiveTriggeredAbility(
new DamageTargetEffect(CreaturesYouControlCount.instance),
false,
"When you do, if you control a red permanent other than {this}, "
+ "he deals damage equal to the number of creatures you control to any target.",
condition
);
reflexive.addTarget(new TargetAnyTarget());
game.fireReflexiveTriggeredAbility(reflexive, source);
return true;
}
}
// Inspired by Mythos of Snapdax
class AjaniNacatlAvengerMinusFourEffect extends OneShotEffect {
private static final List<CardType> cardTypes = Arrays.asList(
CardType.ARTIFACT,
CardType.CREATURE,
CardType.ENCHANTMENT,
CardType.PLANESWALKER
);
AjaniNacatlAvengerMinusFourEffect() {
super(Outcome.Benefit);
staticText = "Each opponent chooses an artifact, a creature, an enchantment and a planeswalker "
+ "from among the nonland permanents they control, then sacrifices the rest.";
}
private AjaniNacatlAvengerMinusFourEffect(final AjaniNacatlAvengerMinusFourEffect effect) {
super(effect);
}
@Override
public AjaniNacatlAvengerMinusFourEffect copy() {
return new AjaniNacatlAvengerMinusFourEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
List<Player> playerList = game
.getState()
.getPlayersInRange(source.getControllerId(), game)
.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.filter(player -> controller.hasOpponent(player.getId(), game))
.collect(Collectors.toList());
Set<UUID> toKeep = new HashSet();
for (Player player : playerList) {
for (CardType cardType : cardTypes) {
String message = cardType.toString().equals("Artifact") ? "an " : "a ";
message += cardType.toString().toLowerCase(Locale.ENGLISH);
FilterPermanent filter = new FilterNonlandPermanent(message);
filter.add(cardType.getPredicate());
filter.add(new ControllerIdPredicate(player.getId()));
if (game.getBattlefield().count(filter, source.getControllerId(), source, game) == 0) {
continue;
}
TargetPermanent target = new TargetPermanent(filter);
target.withNotTarget(true);
player.choose(outcome, target, source, game);
toKeep.add(target.getFirstTarget());
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_NON_LAND, source.getControllerId(), game)) {
if (permanent == null || toKeep.contains(permanent.getId()) || !controller.hasOpponent(permanent.getControllerId(), game)) {
continue;
}
permanent.sacrifice(source, game);
}
return true;
}
}

View file

@ -0,0 +1,61 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Pronoun;
import mage.abilities.common.DiesOneOrMoreCreatureTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.ExileAndReturnSourceEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.game.permanent.token.CatWarrior21Token;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AjaniNacatlPariah extends CardImpl {
public static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.CAT, "other Cats you control");
static {
filter.add(AnotherPredicate.instance);
filter.add(TargetController.YOU.getControllerPredicate());
}
public AjaniNacatlPariah(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.CAT);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
this.secondSideCardClazz = mage.cards.a.AjaniNacatlAvenger.class;
// When Ajani, Nacatl Pariah enters the battlefield, create a 2/1 white Cat Warrior creature token.
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new CatWarrior21Token())));
// Whenever one or more other Cats you control die, you may exile Ajani, then return him to the battlefield transformed under his owner's control.
this.addAbility(new TransformAbility());
this.addAbility(new DiesOneOrMoreCreatureTriggeredAbility(
new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED, Pronoun.HE),
filter
));
}
private AjaniNacatlPariah(final AjaniNacatlPariah card) {
super(card);
}
@Override
public AjaniNacatlPariah copy() {
return new AjaniNacatlPariah(this);
}
}

View file

@ -0,0 +1,62 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AkulTheUnrepentant extends CardImpl {
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("other creatures");
static {
filter.add(AnotherPredicate.instance);
}
public AkulTheUnrepentant(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{R}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.SCORPION);
this.subtype.add(SubType.DRAGON);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Trample
this.addAbility(TrampleAbility.getInstance());
// Sacrifice three other creatures: You may put a creature card from your hand onto the battlefield. Activate only as a sorcery and only once each turn.
this.addAbility(new LimitedTimesPerTurnActivatedAbility(
Zone.BATTLEFIELD,
new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_CREATURE_A),
new SacrificeTargetCost(3, filter)
).setTiming(TimingRule.SORCERY));
}
private AkulTheUnrepentant(final AkulTheUnrepentant card) {
super(card);
}
@Override
public AkulTheUnrepentant copy() {
return new AkulTheUnrepentant(this);
}
}

View file

@ -81,7 +81,7 @@ class AlelaCunningConquerorTriggeredAbility extends DealCombatDamageControlledTr
if (!super.checkTrigger(event, game)) {
return false;
}
Player opponent = game.getPlayer(event.getPlayerId());
Player opponent = game.getPlayer(event.getTargetId());
if (opponent == null) {
return false;
}

View file

@ -0,0 +1,59 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BecomesPlottedSourceTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.PlotAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AloeAlchemist extends CardImpl {
public AloeAlchemist(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.WARLOCK);
this.power = new MageInt(3);
this.toughness = new MageInt(2);
// Trample
this.addAbility(TrampleAbility.getInstance());
// When Aloe Alchemist becomes plotted, target creature gets +3/+2 and gains trample until end of turn.
Ability ability = new BecomesPlottedSourceTriggeredAbility(
new BoostTargetEffect(3, 2, Duration.EndOfTurn)
.setText("target creature gets +3/+2")
);
ability.addTarget(new TargetCreaturePermanent());
ability.addEffect(
new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)
.setText("and gains trample until end of turn")
);
this.addAbility(ability);
// Plot {1}{G}
this.addAbility(new PlotAbility("{1}{G}"));
}
private AloeAlchemist(final AloeAlchemist card) {
super(card);
}
@Override
public AloeAlchemist copy() {
return new AloeAlchemist(this);
}
}

View file

@ -0,0 +1,45 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.keyword.FlashAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.target.common.TargetOpponentsCreaturePermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AmbushGigapede extends CardImpl {
public AmbushGigapede(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}");
this.subtype.add(SubType.INSECT);
this.power = new MageInt(6);
this.toughness = new MageInt(2);
// Flash
this.addAbility(FlashAbility.getInstance());
// When Ambush Gigapede enters the battlefield, target creature an opponent controls gets -2/-2 until end of turn.
Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(-2, -2));
ability.addTarget(new TargetOpponentsCreaturePermanent());
this.addAbility(ability);
}
private AmbushGigapede(final AmbushGigapede card) {
super(card);
}
@Override
public AmbushGigapede copy() {
return new AmbushGigapede(this);
}
}

View file

@ -0,0 +1,97 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.SavedDamageValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.*;
import mage.constants.*;
import mage.abilities.keyword.DoctorsCompanionAbility;
import mage.counters.CounterType;
import mage.filter.common.FilterSuspendedCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInExile;
/**
*
* @author Skiwkr
*/
public final class AmyPond extends CardImpl {
private static final FilterSuspendedCard filter = new FilterSuspendedCard("suspended card you own");
static {
filter.add(TargetController.YOU.getOwnerPredicate());
}
public AmyPond(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Partner with Rory Williams
this.addAbility(new PartnerWithAbility("Rory Williams"));
// Whenever Amy Pond deals combat damage to a player, choose a suspended card you own and remove that many time counters from it.
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AmyPondEffect(SavedDamageValue.MANY),
false, true);
ability.addTarget(new TargetCardInExile(filter).withNotTarget(true));
this.addAbility(ability);
// Doctor's companion
this.addAbility(DoctorsCompanionAbility.getInstance());
}
private AmyPond(final AmyPond card) {
super(card);
}
@Override
public AmyPond copy() {
return new AmyPond(this);
}
}
class AmyPondEffect extends OneShotEffect {
private final DynamicValue numberCounters;
AmyPondEffect(DynamicValue numberCounters) {
super(Outcome.Benefit);
this.numberCounters = numberCounters;
this.staticText= "choose a suspended card you own and remove that many time counters from it";
}
private AmyPondEffect(final AmyPondEffect effect) {
super(effect);
this.numberCounters = effect.numberCounters;
}
@Override
public AmyPondEffect copy() {
return new AmyPondEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = game.getExile().getCard(source.getFirstTarget(), game);
if (card != null) {
card.removeCounters(CounterType.TIME.toString(), (Integer) getValue("damage"), source, game);
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,83 @@
package mage.cards.a;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.mana.AnyColorManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorlessPredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AncientCornucopia extends CardImpl {
private static final FilterSpell filter = new FilterSpell("a spell that's one or more colors");
static {
filter.add(Predicates.not(ColorlessPredicate.instance));
}
public AncientCornucopia(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}");
// Whenever you cast a spell that's one or more colors, you may gain 1 life for each of that spell's colors. Do this only once each turn.
this.addAbility(new SpellCastControllerTriggeredAbility(
new GainLifeEffect(AncientCornucopiaValue.instance)
.setText("gain 1 life for each of that spell's colors"), filter, true
).setDoOnlyOnceEachTurn(true));
// {T}: Add one mana of any color.
this.addAbility(new AnyColorManaAbility());
}
private AncientCornucopia(final AncientCornucopia card) {
super(card);
}
@Override
public AncientCornucopia copy() {
return new AncientCornucopia(this);
}
}
enum AncientCornucopiaValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return Optional
.ofNullable(effect.getValue("spellCast"))
.map(Spell.class::cast)
.map(spell -> spell.getColor(game))
.map(ObjectColor::getColorCount)
.orElse(0);
}
@Override
public AncientCornucopiaValue copy() {
return this;
}
@Override
public String getMessage() {
return "of that spell's colors";
}
@Override
public String toString() {
return "1";
}
}

View file

@ -0,0 +1,66 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.EncoreAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterPermanentCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AngelOfIndemnity extends CardImpl {
private static final FilterCard filter
= new FilterPermanentCard("permanent card with mana value 4 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5));
}
public AngelOfIndemnity(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
this.subtype.add(SubType.ANGEL);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Lifelink
this.addAbility(LifelinkAbility.getInstance());
// When Angel of Indemnity enters the battlefield, return target permanent card with mana value 4 or less from your graveyard to the battlefield.
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect());
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability);
// Encore {6}{W}{W}
this.addAbility(new EncoreAbility(new ManaCostsImpl<>("{6}{W}{W}")));
}
private AngelOfIndemnity(final AngelOfIndemnity card) {
super(card);
}
@Override
public AngelOfIndemnity copy() {
return new AngelOfIndemnity(this);
}
}

View file

@ -68,7 +68,7 @@ class AngelheartVialTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
}
@Override

View file

@ -0,0 +1,79 @@
package mage.cards.a;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.token.MercenaryToken;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AngelicSellSword extends CardImpl {
public AngelicSellSword(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
this.subtype.add(SubType.ANGEL);
this.subtype.add(SubType.MERCENARY);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// Whenever Angelic Sell-Sword or another nontoken creature enters the battlefield under your control, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery."
this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
new CreateTokenEffect(new MercenaryToken()),
StaticFilters.FILTER_CREATURE_NON_TOKEN, false, true
));
// Whenever Angelic Sell-Sword attacks, if its power is 6 or greater, draw a card.
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1)),
AngelicSellSwordCondition.instance, "Whenever {this} attacks, " +
"if its power is 6 or greater, draw a card."
));
}
private AngelicSellSword(final AngelicSellSword card) {
super(card);
}
@Override
public AngelicSellSword copy() {
return new AngelicSellSword(this);
}
}
enum AngelicSellSwordCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
return Optional
.ofNullable(source.getSourcePermanentOrLKI(game))
.map(MageObject::getPower)
.map(MageInt::getValue)
.orElse(0) >= 6;
}
}

View file

@ -0,0 +1,36 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.keyword.DeathtouchAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AnkleBiter extends CardImpl {
public AnkleBiter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
this.subtype.add(SubType.SNAKE);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Deathtouch
this.addAbility(DeathtouchAbility.getInstance());
}
private AnkleBiter(final AnkleBiter card) {
super(card);
}
@Override
public AnkleBiter copy() {
return new AnkleBiter(this);
}
}

View file

@ -0,0 +1,67 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BecomesTappedSourceTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.FlashAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterPermanentCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AnnieFlashTheVeteran extends CardImpl {
private static final FilterPermanentCard filter =
new FilterPermanentCard("permanent card with mana value 3 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3));
}
public AnnieFlashTheVeteran(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(4);
this.toughness = new MageInt(5);
// Flash
this.addAbility(FlashAbility.getInstance());
// When Annie Flash, the Veteran enters the battlefield, if you cast it, return target permanent card with mana value 3 or less from your graveyard to the battlefield tapped.
Ability ability = new ConditionalInterveningIfTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(true)),
CastFromEverywhereSourceCondition.instance,
"When {this} enters the battlefield, if you cast it, "
+ "return target permanent card with mana value 3 or less from your graveyard to the battlefield tapped"
);
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability);
// Whenever Annie Flash becomes tapped, exile the top two cards of your library. You may play those cards this turn.
this.addAbility(new BecomesTappedSourceTriggeredAbility(new ExileTopXMayPlayUntilEffect(2, Duration.EndOfTurn)));
}
private AnnieFlashTheVeteran(final AnnieFlashTheVeteran card) {
super(card);
}
@Override
public AnnieFlashTheVeteran copy() {
return new AnnieFlashTheVeteran(this);
}
}

View file

@ -0,0 +1,96 @@
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.NumberOfTriggersEvent;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AnnieJoinsUp extends CardImpl {
private static final FilterPermanent filter
= new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls");
static {
filter.add(TargetController.OPPONENT.getControllerPredicate());
}
public AnnieJoinsUp(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{G}{W}");
this.supertype.add(SuperType.LEGENDARY);
// When Annie Joins Up enters the battlefield, it deals 5 damage to target creature or planeswalker an opponent controls.
Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(5));
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
// If a triggered ability of a legendary creature you control triggers, that ability triggers an additional time.
this.addAbility(new SimpleStaticAbility(new AnnieJoinsUpEffect()));
}
private AnnieJoinsUp(final AnnieJoinsUp card) {
super(card);
}
@Override
public AnnieJoinsUp copy() {
return new AnnieJoinsUp(this);
}
}
class AnnieJoinsUpEffect extends ReplacementEffectImpl {
AnnieJoinsUpEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "if a triggered ability of a legendary creature you control triggers, " +
"that ability triggers an additional time";
}
private AnnieJoinsUpEffect(final AnnieJoinsUpEffect effect) {
super(effect);
}
@Override
public AnnieJoinsUpEffect copy() {
return new AnnieJoinsUpEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!(event instanceof NumberOfTriggersEvent)) {
return false;
}
Permanent permanent = game.getPermanent(((NumberOfTriggersEvent) event).getSourceId());
return permanent != null
&& permanent.isControlledBy(source.getControllerId())
&& permanent.isLegendary(game);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(CardUtil.overflowInc(event.getAmount(), 1));
return false;
}
}

View file

@ -0,0 +1,89 @@
package mage.cards.a;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileThenReturnTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.targetpointer.FixedTargets;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author Susucr
*/
public final class AnotherRound extends CardImpl {
public AnotherRound(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{2}{W}");
// Exile any number of creatures you control, then return them to the battlefield under their owner's control. Then repeat this process X times.
this.getSpellAbility().addEffect(new AnotherRoundEffect());
}
private AnotherRound(final AnotherRound card) {
super(card);
}
@Override
public AnotherRound copy() {
return new AnotherRound(this);
}
}
class AnotherRoundEffect extends OneShotEffect {
public AnotherRoundEffect() {
super(Outcome.Benefit);
staticText = "Exile any number of creatures you control, "
+ "then return them to the battlefield under their owner's control. "
+ "Then repeat this process X more times.";
}
private AnotherRoundEffect(final AnotherRoundEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
int xValue = ManacostVariableValue.REGULAR.calculate(game, source, this);
TargetControlledCreaturePermanent target =
new TargetControlledCreaturePermanent(
0, Integer.MAX_VALUE,
StaticFilters.FILTER_CONTROLLED_CREATURE, true
);
for (int i = 0; i <= xValue; ++i) {
target.clearChosen();
controller.chooseTarget(Outcome.Benefit, target, source, game);
new ExileThenReturnTargetEffect(false, true)
.setTargetPointer(new FixedTargets(
target.getTargets()
.stream()
.map(id -> new MageObjectReference(id, game))
.collect(Collectors.toList())
)).apply(game, source);
game.getState().processAction(game);
}
return true;
}
@Override
public AnotherRoundEffect copy() {
return new AnotherRoundEffect(this);
}
}

View file

@ -0,0 +1,184 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.HasteAbility;
import mage.cards.*;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.filter.common.FilterArtifactPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInExile;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.watchers.Watcher;
/**
* @author Cguy7777
*/
public final class AnzragsRampage extends CardImpl {
private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifacts you don't control");
static {
filter.add(TargetController.NOT_YOU.getControllerPredicate());
}
private static final ValueHint hint = new ValueHint(
"Artifacts put into graveyards from the battlefield this turn", AnzragsRampageValue.instance);
public AnzragsRampage(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}");
// Destroy all artifacts you don't control, then exile the top X cards of your library, where X is the number of artifacts that were put into graveyards from the battlefield this turn.
// You may put a creature card exiled this way onto the battlefield. It gains haste. Return it to your hand at the beginning of the next end step.
this.getSpellAbility().addEffect(new DestroyAllEffect(filter));
this.getSpellAbility().addEffect(new AnzragsRampageEffect().concatBy(", then"));
this.getSpellAbility().addWatcher(new AnzragsRampageWatcher());
this.getSpellAbility().addHint(hint);
}
private AnzragsRampage(final AnzragsRampage card) {
super(card);
}
@Override
public AnzragsRampage copy() {
return new AnzragsRampage(this);
}
}
class AnzragsRampageWatcher extends Watcher {
private int artifactsDied;
AnzragsRampageWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent() && zEvent.getTarget().isArtifact(game)) {
artifactsDied++;
}
}
}
@Override
public void reset() {
super.reset();
artifactsDied = 0;
}
public int getArtifactsDied() {
return artifactsDied;
}
}
enum AnzragsRampageValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
AnzragsRampageWatcher watcher = game.getState().getWatcher(AnzragsRampageWatcher.class);
if (watcher == null) {
return 0;
}
return watcher.getArtifactsDied();
}
@Override
public AnzragsRampageValue copy() {
return instance;
}
@Override
public String getMessage() {
return "";
}
}
class AnzragsRampageEffect extends OneShotEffect {
AnzragsRampageEffect() {
super(Outcome.PutCreatureInPlay);
staticText = "exile the top X cards of your library, where X is the number of artifacts " +
"that were put into graveyards from the battlefield this turn. " +
"You may put a creature card exiled this way onto the battlefield. It gains haste. " +
"Return it to your hand at the beginning of the next end step";
}
private AnzragsRampageEffect(final AnzragsRampageEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
// Exile the top X cards of your library,
// where X is the number of artifacts that were put into graveyards from the battlefield this turn.
Cards cards = new CardsImpl(controller.getLibrary()
.getTopCards(game, AnzragsRampageValue.instance.calculate(game, source, this)));
controller.moveCardsToExile(
cards.getCards(game), source, game, true,
CardUtil.getExileZoneId(game, source),
CardUtil.createObjectRealtedWindowTitle(source, game, null));
// You may put a creature card exiled this way onto the battlefield.
TargetCard targetCard = new TargetCardInExile(
0, 1, StaticFilters.FILTER_CARD_CREATURE, CardUtil.getExileZoneId(game, source));
targetCard.withNotTarget(true);
controller.choose(outcome, targetCard, source, game);
Card card = game.getCard(targetCard.getFirstTarget());
if (card == null) {
return true;
}
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
Permanent permanent = game.getPermanent(card.getId());
if (permanent == null) {
return true;
}
// It gains haste.
ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
hasteEffect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(hasteEffect, source);
// Return it to your hand at the beginning of the next end step.
ReturnToHandTargetEffect returnToHandEffect = new ReturnToHandTargetEffect();
returnToHandEffect.setText("return it to your hand");
returnToHandEffect.setTargetPointer(new FixedTarget(permanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
}
return true;
}
@Override
public AnzragsRampageEffect copy() {
return new AnzragsRampageEffect(this);
}
}

View file

@ -0,0 +1,83 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author justinjohnson14
*/
public final class ArcadeGannon extends CardImpl {
private static final FilterCard filter = new FilterCard("an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on {this}");
static{
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
SubType.HUMAN.getPredicate()
));
filter.add(ArcadeGannonPredicate.instance);
}
public ArcadeGannon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.DOCTOR);
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// {T}: Draw a card, then discard a card. Put a quest counter on Arcade Gannon.
Ability ability = (new SimpleActivatedAbility(new DrawDiscardControllerEffect(1,1), new TapSourceCost()));
ability.addEffect(new AddCountersSourceEffect(CounterType.QUEST.createInstance(1)));
this.addAbility(ability);
// For Auld Lang Syne -- Once during each of your turns, you may cast an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on Arcade Gannon.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter).withFlavorWord("For Auld Lang Syne"));
}
private ArcadeGannon(final ArcadeGannon card) {
super(card);
}
@Override
public ArcadeGannon copy() {
return new ArcadeGannon(this);
}
}
enum ArcadeGannonPredicate implements ObjectSourcePlayerPredicate<Card> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
Permanent sourcePermanent = input.getSource().getSourcePermanentOrLKI(game);
return sourcePermanent != null && input.getObject().getManaValue() <= sourcePermanent.getCounters(game).getCount(CounterType.QUEST);
}
@Override
public String toString() {
return "mana value less than or equal to {this}'s power";
}
}

View file

@ -0,0 +1,51 @@
package mage.cards.a;
import mage.abilities.effects.common.CipherEffect;
import mage.abilities.effects.common.MayCastTargetCardEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.target.common.TargetCardInOpponentsGraveyard;
import java.util.UUID;
/**
* @author Susucr
*/
public final class ArcaneHeist extends CardImpl {
private static final FilterCard filter = new FilterCard("instant or sorcery card from an opponent's graveyard");
static {
filter.add(Predicates.or(
CardType.INSTANT.getPredicate(),
CardType.SORCERY.getPredicate()));
}
public ArcaneHeist(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}");
// You may cast target instant or sorcery card from an opponent's graveyard without paying its mana cost. If that spell would be put into their graveyard, exile it instead.
this.getSpellAbility().addEffect(
new MayCastTargetCardEffect(CastManaAdjustment.WITHOUT_PAYING_MANA_COST, true)
.setText("You may cast target instant or sorcery card from an opponent's graveyard without paying its mana cost. "
+ "If that spell would be put into their graveyard, exile it instead.")
);
this.getSpellAbility().addTarget(new TargetCardInOpponentsGraveyard(filter));
// Cipher
this.getSpellAbility().addEffect(new CipherEffect());
}
private ArcaneHeist(final ArcaneHeist card) {
super(card);
}
@Override
public ArcaneHeist copy() {
return new ArcaneHeist(this);
}
}

View file

@ -84,7 +84,7 @@ class ArcbondDelayedTriggeredAbility extends DelayedTriggeredAbility {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT;
}
@Override

View file

@ -0,0 +1,96 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.condition.common.SaddledCondition;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.keyword.FlashbackAbility;
import mage.abilities.keyword.SaddleAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class ArchmagesNewt extends CardImpl {
public ArchmagesNewt(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
this.subtype.add(SubType.SALAMANDER);
this.subtype.add(SubType.MOUNT);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Whenever Archmage's Newt deals combat damage to a player, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. That card gains flashback {0} until end of turn instead if Archmage's Newt is saddled.
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ArchmagesNewtEffect(), false);
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD));
this.addAbility(ability);
// Saddle 3
this.addAbility(new SaddleAbility(3));
}
private ArchmagesNewt(final ArchmagesNewt card) {
super(card);
}
@Override
public ArchmagesNewt copy() {
return new ArchmagesNewt(this);
}
}
class ArchmagesNewtEffect extends ContinuousEffectImpl {
private boolean saddled = false;
ArchmagesNewtEffect() {
super(Duration.EndOfTurn, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.staticText = "target instant or sorcery card in your graveyard gains flashback until end of turn. " +
"The flashback cost is equal to its mana cost. That card gains flashback {0} until end of turn instead if {this} is saddled";
}
private ArchmagesNewtEffect(final ArchmagesNewtEffect effect) {
super(effect);
this.saddled = effect.saddled;
}
@Override
public ArchmagesNewtEffect copy() {
return new ArchmagesNewtEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
this.saddled = SaddledCondition.instance.apply(game, source);
}
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getCard(getTargetPointer().getFirst(game, source));
if (card == null) {
return false;
}
FlashbackAbility ability;
if (saddled) {
ability = new FlashbackAbility(card, new GenericManaCost(0));
} else {
ability = new FlashbackAbility(card, card.getManaCost());
}
ability.setSourceId(card.getId());
ability.setControllerId(card.getOwnerId());
game.getState().addOtherAbility(card, ability);
return true;
}
}

View file

@ -4,6 +4,7 @@ import mage.MageInt;
import mage.abilities.common.CantBlockAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.IndestructibleAbility;
import mage.abilities.keyword.SquadAbility;
@ -28,7 +29,7 @@ public final class ArcoFlagellant extends CardImpl {
this.toughness = new MageInt(1);
// Squad {2}
this.addAbility(new SquadAbility());
this.addAbility(new SquadAbility(new GenericManaCost(2)));
// Arco-Flagellant can't block.
this.addAbility(new CantBlockAbility());

View file

@ -0,0 +1,96 @@
package mage.cards.a;
import mage.MageObjectReference;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AridArchway extends CardImpl {
public AridArchway(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.subtype.add(SubType.DESERT);
// Arid Archway enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility());
// When Arid Archway enters the battlefield, return a land you control to its owner's hand. If another Desert was returned this way, surveil 1.
this.addAbility(new EntersBattlefieldTriggeredAbility(new AridArchwayEffect()));
// {T}: Add {C}{C}.
this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost()));
}
private AridArchway(final AridArchway card) {
super(card);
}
@Override
public AridArchway copy() {
return new AridArchway(this);
}
}
class AridArchwayEffect extends OneShotEffect {
AridArchwayEffect() {
super(Outcome.Benefit);
staticText = "return a land you control to its owner's hand. If another Desert was returned this way, surveil 1";
}
private AridArchwayEffect(final AridArchwayEffect effect) {
super(effect);
}
@Override
public AridArchwayEffect copy() {
return new AridArchwayEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
if (!game.getBattlefield().contains(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, source, game, 1)) {
return false;
}
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
TargetPermanent target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND);
target.withNotTarget(true);
player.choose(outcome, target, source, game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent == null) {
return false;
}
boolean flag = permanent.hasSubtype(SubType.DESERT, game)
&& !new MageObjectReference(permanent, game)
.refersTo(source, game);
player.moveCards(permanent, Zone.HAND, source, game);
if (flag) {
player.surveil(1, source, game);
}
return true;
}
}

View file

@ -0,0 +1,49 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class ArmoredArmadillo extends CardImpl {
public ArmoredArmadillo(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
this.subtype.add(SubType.ARMADILLO);
this.power = new MageInt(0);
this.toughness = new MageInt(4);
// Ward {1}
this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}")));
// {3}{W}: Armored Armadillo gets +X/+0 until end of turn, where X is its toughness.
this.addAbility(new SimpleActivatedAbility(
new BoostSourceEffect(
SourcePermanentToughnessValue.getInstance(), StaticValue.get(0), Duration.EndOfTurn
).setText("{this} gets +X/+0 until end of turn, where X is its toughness."),
new ManaCostsImpl<>("{3}{W}")));
}
private ArmoredArmadillo(final ArmoredArmadillo card) {
super(card);
}
@Override
public ArmoredArmadillo copy() {
return new ArmoredArmadillo(this);
}
}

View file

@ -74,7 +74,7 @@ class AsLuckWouldHaveItTriggeredAbility extends TriggeredAbilityImpl {
// Any die roll with a numerical result will add luck counters to As Luck Would Have It.
// Rolling the planar die will not cause the second ability to trigger.
// (2018-01-19)
if (this.isControlledBy(event.getPlayerId()) && drEvent.getRollDieType() == RollDieType.NUMERICAL) {
if (this.isControlledBy(event.getTargetId()) && drEvent.getRollDieType() == RollDieType.NUMERICAL) {
// silver border card must look for "result" instead "natural result"
this.getEffects().setValue("rolled", drEvent.getResult());
return true;

View file

@ -3,21 +3,23 @@ package mage.cards.a;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.abilities.hint.Hint;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.Watcher;
import mage.watchers.common.OnceEachTurnCastWatcher;
import java.util.*;
import java.util.UUID;
/**
* @author xenohedron
@ -33,9 +35,9 @@ public final class AssembleThePlayers extends CardImpl {
// Once each turn, you may cast a creature spell with power 2 or less from the top of your library.
this.addAbility(
new SimpleStaticAbility(new AssembleThePlayersPlayTopEffect())
.setIdentifier(MageIdentifier.AssembleThePlayersWatcher)
.addHint(AssembleThePlayersHint.instance),
new AssembleThePlayersWatcher()
.setIdentifier(MageIdentifier.OnceEachTurnCastWatcher)
.addHint(OnceEachTurnCastWatcher.getHint()),
new OnceEachTurnCastWatcher()
// all based on Johann, Apprentice Sorcerer
);
@ -51,30 +53,6 @@ public final class AssembleThePlayers extends CardImpl {
}
}
enum AssembleThePlayersHint implements Hint {
instance;
@Override
public String getText(Game game, Ability ability) {
AssembleThePlayersWatcher watcher = game.getState().getWatcher(AssembleThePlayersWatcher.class);
if (watcher != null) {
boolean used = watcher.isAbilityUsed(ability.getControllerId(), new MageObjectReference(ability.getSourceId(), game));
if (used) {
Player player = game.getPlayer(ability.getControllerId());
if (player != null) {
return "A spell has been cast by " + player.getLogName() + " with {this} this turn.";
}
}
}
return "";
}
@Override
public AssembleThePlayersHint copy() {
return this;
}
}
class AssembleThePlayersPlayTopEffect extends AsThoughEffectImpl {
AssembleThePlayersPlayTopEffect() {
@ -98,62 +76,41 @@ class AssembleThePlayersPlayTopEffect extends AsThoughEffectImpl {
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
// Only applies for the controller of the ability.
if (!affectedControllerId.equals(source.getControllerId())) {
return false;
}
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
Player controller = game.getPlayer(source.getControllerId());
AssembleThePlayersWatcher watcher = game.getState().getWatcher(AssembleThePlayersWatcher.class);
Permanent sourceObject = game.getPermanent(source.getSourceId());
if (controller == null || watcher == null || sourceObject == null) {
OnceEachTurnCastWatcher watcher = game.getState().getWatcher(OnceEachTurnCastWatcher.class);
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (controller == null || sourcePermanent == null || watcher == null) {
return false;
}
// Only applies for the controller of the ability.
if (!playerId.equals(source.getControllerId())) {
return false;
}
// Has the ability already been used this turn by the player?
if (watcher.isAbilityUsed(controller.getId(), new MageObjectReference(sourceObject, game))) {
if (watcher.isAbilityUsed(controller.getId(), new MageObjectReference(sourcePermanent, game))) {
return false;
}
Card card = game.getCard(objectId);
Card topCard = controller.getLibrary().getFromTop(game);
// Is the card attempted to be played the top card of the library?
if (card == null || topCard == null || !topCard.getId().equals(card.getMainCard().getId())) {
return false;
}
// Only works for creatures with power 2 or less
return card.isCreature(game) && card.getPower().getValue() <=2;
}
}
class AssembleThePlayersWatcher extends Watcher {
// player -> set of all permanent's mor that already used their once per turn Approval.
private final Map<UUID, Set<MageObjectReference>> usedFrom = new HashMap<>();
public AssembleThePlayersWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
UUID playerId = event.getPlayerId();
if (event.getType() == GameEvent.EventType.SPELL_CAST
&& event.hasApprovingIdentifier(MageIdentifier.AssembleThePlayersWatcher)
&& playerId != null) {
usedFrom.computeIfAbsent(playerId, k -> new HashSet<>())
.add(event.getAdditionalReference().getApprovingMageObjectReference());
if (affectedAbility instanceof SpellAbility) {
SpellAbility spellAbility = (SpellAbility) affectedAbility;
if (spellAbility.getManaCosts().isEmpty()
|| !spellAbility.spellCanBeActivatedRegularlyNow(playerId, game)) {
return false;
}
Card cardToCheck = spellAbility.getCharacteristics(game);
// Only works for creatures with power 2 or less
return cardToCheck.isCreature(game) && cardToCheck.getPower().getValue() <= 2;
}
}
@Override
public void reset() {
super.reset();
usedFrom.clear();
}
public boolean isAbilityUsed(UUID playerId, MageObjectReference mor) {
return usedFrom.getOrDefault(playerId, Collections.emptySet()).contains(mor);
return false;
}
}

View file

@ -0,0 +1,134 @@
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.common.AttachedToCreatureSourceTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
import mage.abilities.keyword.EquipAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInExile;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AssimilationAegis extends CardImpl {
public AssimilationAegis(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}{U}");
this.subtype.add(SubType.EQUIPMENT);
// When Assimilation Aegis enters the battlefield, exile up to one target creature until Assimilation Aegis leaves the battlefield.
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect());
ability.addTarget(new TargetCreaturePermanent(0, 1));
this.addAbility(ability);
// Whenever Assimilation Aegis becomes attached to a creature, for as long as Assimilation Aegis remains attached to it, that creature becomes a copy of a creature card exiled with Assimilation Aegis.
this.addAbility(new AttachedToCreatureSourceTriggeredAbility(new AssimilationAegisEffect(), false));
// Equip {2}
this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2)));
}
private AssimilationAegis(final AssimilationAegis card) {
super(card);
}
@Override
public AssimilationAegis copy() {
return new AssimilationAegis(this);
}
}
// Similar to Blade of Shared Souls.
class AssimilationAegisEffect extends OneShotEffect {
AssimilationAegisEffect() {
super(Outcome.Benefit);
staticText = "for as long as {this} remains attached to it, " +
"that creature becomes a copy of a creature card exiled with {this}";
}
private AssimilationAegisEffect(final AssimilationAegisEffect effect) {
super(effect);
}
@Override
public AssimilationAegisEffect copy() {
return new AssimilationAegisEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent attachedPermanent = (Permanent) getValue("attachedPermanent");
Permanent equipment = source.getSourcePermanentIfItStillExists(game);
if (attachedPermanent == null
|| equipment == null
|| !equipment.isAttachedTo(attachedPermanent.getId())) {
return false;
}
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
UUID exileId = CardUtil.getExileZoneId(game, source);
TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD_CREATURE, exileId);
target.withNotTarget(true);
if (!target.choose(Outcome.Benefit, player.getId(), source.getId(), source, game)) {
return false;
}
Card copyCard = game.getCard(target.getFirstTarget());
if (copyCard == null) {
return false;
}
game.addEffect(new AssimilationAegisCopyEffect(copyCard, attachedPermanent), source);
return true;
}
}
// Similar to Blade of Shared Souls.
class AssimilationAegisCopyEffect extends CopyEffect {
AssimilationAegisCopyEffect(Card copyCard, Permanent attachedPermanent) {
super(Duration.Custom, copyCard, attachedPermanent.getId());
}
private AssimilationAegisCopyEffect(final AssimilationAegisCopyEffect effect) {
super(effect);
}
@Override
public AssimilationAegisCopyEffect copy() {
return new AssimilationAegisCopyEffect(this);
}
@Override
public boolean isInactive(Ability source, Game game) {
if (super.isInactive(source, game)) {
return true;
}
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (sourcePermanent == null || !sourcePermanent.isAttachedTo(this.copyToObjectId)) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,52 @@
package mage.cards.a;
import mage.abilities.common.CommittedCrimeTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.MyTurnCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.OutlawPredicate;
import mage.game.permanent.token.MercenaryToken;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AtKnifepoint extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(OutlawPredicate.instance);
}
public AtKnifepoint(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{R}");
// As long as it's your turn, outlaws you control have first strike.
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter),
MyTurnCondition.instance, "as long as it's your turn, outlaws you control have first strike"
)));
// Whenever you commit a crime, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery." This ability triggers only once each turn.
this.addAbility(new CommittedCrimeTriggeredAbility(new CreateTokenEffect(new MercenaryToken())).setTriggersOnceEachTurn(true));
}
private AtKnifepoint(final AtKnifepoint card) {
super(card);
}
@Override
public AtKnifepoint copy() {
return new AtKnifepoint(this);
}
}

View file

@ -8,7 +8,7 @@ import mage.abilities.condition.common.CovenCondition;
import mage.abilities.decorator.ConditionalAsThoughEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect;
import mage.abilities.hint.common.CovenHint;
import mage.constants.AbilityWord;
import mage.constants.SubType;
@ -41,11 +41,11 @@ public final class AugurOfAutumn extends CardImpl {
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may play lands from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
this.addAbility(new SimpleStaticAbility(new PlayFromTopOfLibraryEffect(filter)));
// Coven As long as you control three or more creatures with different powers, you may cast creature spells from the top of your library.
Effect effect = new ConditionalAsThoughEffect(
new PlayTheTopCardEffect(TargetController.YOU, filter2, false),
new PlayFromTopOfLibraryEffect(filter2),
CovenCondition.instance
);
effect.setText("As long as you control three or more creatures with different powers, you may cast creature spells from the top of your library");

View file

@ -0,0 +1,85 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.MorphManacostVariableValue;
import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.abilities.keyword.DisguiseAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.target.Target;
import mage.target.common.TargetCardInGraveyardBattlefieldOrStack;
import mage.target.targetadjustment.TargetAdjuster;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AureliasVindicator extends CardImpl {
public AureliasVindicator(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
this.subtype.add(SubType.ANGEL);
this.power = new MageInt(4);
this.toughness = new MageInt(2);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Lifelink
this.addAbility(LifelinkAbility.getInstance());
// Ward {2}
this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}")));
// Disguise {X}{3}{W}
this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{X}{3}{W}")));
// When Aurelia's Vindicator is turned face up, exile up to X other target creatures from the battlefield and/or creature cards from graveyards.
Ability ability = new TurnedFaceUpSourceTriggeredAbility(new ExileTargetForSourceEffect()
.setText("exile up to X other target creatures from the battlefield and/or creature cards from graveyards"));
ability.setTargetAdjuster(AureliasVindicatorAdjuster.instance);
this.addAbility(ability);
// When Aurelia's Vindicator leaves the battlefield, return the exiled cards to their owners' hands.
this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.HAND)
.withText(true, true, false), false));
}
private AureliasVindicator(final AureliasVindicator card) {
super(card);
}
@Override
public AureliasVindicator copy() {
return new AureliasVindicator(this);
}
}
enum AureliasVindicatorAdjuster implements TargetAdjuster {
instance;
@Override
public void adjustTargets(Ability ability, Game game) {
ability.getTargets().clear();
int xValue = MorphManacostVariableValue.instance.calculate(game, ability, null);
Target target = new TargetCardInGraveyardBattlefieldOrStack(
0, xValue, StaticFilters.FILTER_CARD_CREATURE, StaticFilters.FILTER_PERMANENT_CREATURES
);
ability.addTarget(target);
}
}

View file

@ -0,0 +1,104 @@
package mage.cards.a;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.PlotAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.CastFromZonePredicate;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetSpell;
import java.util.UUID;
/**
* @author Susucr
*/
public final class AvenInterrupter extends CardImpl {
private static final FilterCard filter = new FilterCard("spells your opponents cast from graveyards or from exile");
static {
filter.add(Predicates.or(
new CastFromZonePredicate(Zone.GRAVEYARD),
new CastFromZonePredicate(Zone.EXILED)
));
}
public AvenInterrupter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}");
this.subtype.add(SubType.BIRD);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Flash
this.addAbility(FlashAbility.getInstance());
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Aven Interrupter enters the battlefield, exile target spell. It becomes plotted.
Ability ability = new EntersBattlefieldTriggeredAbility(new AvenInterrupterEffect());
ability.addTarget(new TargetSpell());
this.addAbility(ability);
// Spells your opponents cast from graveyards or from exile cost 2 more to cast.
this.addAbility(new SimpleStaticAbility(
new SpellsCostIncreasingAllEffect(2, filter, TargetController.OPPONENT)
));
}
private AvenInterrupter(final AvenInterrupter card) {
super(card);
}
@Override
public AvenInterrupter copy() {
return new AvenInterrupter(this);
}
}
class AvenInterrupterEffect extends OneShotEffect {
AvenInterrupterEffect() {
super(Outcome.Detriment);
staticText = "exile target spell. It becomes plotted";
}
private AvenInterrupterEffect(final AvenInterrupterEffect effect) {
super(effect);
}
@Override
public AvenInterrupterEffect copy() {
return new AvenInterrupterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || sourceObject == null) {
return false;
}
Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
if (spell == null) {
return false;
}
return PlotAbility.doExileAndPlotCard(spell, game, source);
}
}

View file

@ -0,0 +1,53 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.OutlawPredicate;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.TargetAdjuster;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BackInTown extends CardImpl {
public BackInTown(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{2}{B}");
// Return X target outlaw creature cards from your graveyard to the battlefield.
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()
.setText("return X target outlaw creature cards from your graveyard to the battlefield"));
this.getSpellAbility().setTargetAdjuster(BackInTownAdjuster.instance);
}
private BackInTown(final BackInTown card) {
super(card);
}
@Override
public BackInTown copy() {
return new BackInTown(this);
}
}
enum BackInTownAdjuster implements TargetAdjuster {
instance;
private static final FilterCard filter = new FilterCard("outlaw cards");
static {
filter.add(OutlawPredicate.instance);
}
@Override
public void adjustTargets(Ability ability, Game game) {
ability.getTargets().clear();
ability.addTarget(new TargetCardInYourGraveyard(ability.getManaCostsToPay().getX(), filter));
}
}

View file

@ -0,0 +1,41 @@
package mage.cards.b;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterPermanentCard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetpointer.SecondTargetPointer;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BadlandsRevival extends CardImpl {
private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard");
public BadlandsRevival(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{G}");
// Return up to one target creature card from your graveyard to the battlefield. Return up to one target permanent card from your graveyard to your hand.
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect().setTargetPointer(new SecondTargetPointer()));
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 1, filter));
}
private BadlandsRevival(final BadlandsRevival card) {
super(card);
}
@Override
public BadlandsRevival copy() {
return new BadlandsRevival(this);
}
}

View file

@ -0,0 +1,52 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.common.CommittedCrimeTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.mana.AnyColorManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.counters.CounterType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BanditsHaul extends CardImpl {
public BanditsHaul(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// Whenever you commit a crime, put a loot counter on Bandit's Haul. This ability triggers only once each turn.
this.addAbility(new CommittedCrimeTriggeredAbility(
new AddCountersSourceEffect(CounterType.LOOT.createInstance())
).setTriggersOnceEachTurn(true));
// {T}: Add one mana of any color.
this.addAbility(new AnyColorManaAbility());
// {2}, {T}, Remove two loot counters from Bandit's Haul: Draw a card.
Ability ability = new SimpleActivatedAbility(
new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
);
ability.addCost(new TapSourceCost());
ability.addCost(new RemoveCountersSourceCost(CounterType.LOOT.createInstance(2)));
this.addAbility(ability);
}
private BanditsHaul(final BanditsHaul card) {
super(card);
}
@Override
public BanditsHaul copy() {
return new BanditsHaul(this);
}
}

View file

@ -0,0 +1,63 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldOneOrMoreTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.permanent.token.VampireRogueToken;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BaronBertramGraywater extends CardImpl {
static final FilterPermanent filter = new FilterPermanent("tokens");
static {
filter.add(TokenPredicate.TRUE);
}
public BaronBertramGraywater(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.NOBLE);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Whenever one or more tokens enter the battlefield under your control, create a 1/1 black Vampire Rogue creature token with lifelink. This ability triggers only once each turn.
this.addAbility(new EntersBattlefieldOneOrMoreTriggeredAbility(
new CreateTokenEffect(new VampireRogueToken()), filter, TargetController.YOU
).setTriggersOnceEachTurn(true));
// {1}{B}, Sacrifice another creature or artifact: Draw a card.
Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{1}{B}"));
ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT_SHORT_TEXT));
this.addAbility(ability);
}
private BaronBertramGraywater(final BaronBertramGraywater card) {
super(card);
}
@Override
public BaronBertramGraywater copy() {
return new BaronBertramGraywater(this);
}
}

View file

@ -59,7 +59,7 @@ public final class BasandraBattleSeraph extends CardImpl {
class BasandraBattleSeraphEffect extends ContinuousRuleModifyingEffectImpl {
public BasandraBattleSeraphEffect() {
super(Duration.EndOfTurn, Outcome.Neutral);
super(Duration.WhileOnBattlefield, Outcome.Neutral);
staticText = "Players can't cast spells during combat";
}

View file

@ -99,10 +99,11 @@ enum BatColonyValue implements DynamicValue {
/**
* Inspired by {@link mage.watchers.common.ManaPaidSourceWatcher}
* If more cards like Bat Colony care for mana spent by Caves in the future, best to refactor the tracking there.
* If more cards like Bat Colony care for mana produced by Caves in the future, best to refactor the tracking there.
* For now the assumption is that it is a 1of, so don't want to track it in any game.
*/
class BatColonyWatcher extends Watcher {
private static final class CaveManaPaidTracker implements Serializable, Copyable<CaveManaPaidTracker> {
private int caveMana = 0;

View file

@ -0,0 +1,78 @@
package mage.cards.b;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.condition.common.ModeChoiceSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ChooseModeEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldWithCounterTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetControlledCreaturePermanent;
/**
* @author Cguy7777
*/
public final class BattleOfHooverDam extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard();
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4));
}
public BattleOfHooverDam(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}");
// As Battle of Hoover Dam enters the battlefield, choose NCR or Legion.
this.addAbility(new AsEntersBattlefieldAbility(
new ChooseModeEffect("NCR or Legion?", "NCR", "Legion")));
// * NCR -- At the beginning of your end step, return target creature card with mana value 3 or less
// from your graveyard to the battlefield with a finality counter on it.
Ability ncrAbility = new ConditionalTriggeredAbility(
new BeginningOfEndStepTriggeredAbility(
new ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(CounterType.FINALITY.createInstance()),
TargetController.YOU,
false),
new ModeChoiceSourceCondition("NCR"),
"&bull NCR &mdash; At the beginning of your end step, return target creature card with " +
"mana value 3 or less from your graveyard to the battlefield with a finality counter on it.");
ncrAbility.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ncrAbility);
// * Legion -- Whenever a creature you control dies, put two +1/+1 counters on target creature you control.
Ability legionAbility = new ConditionalTriggeredAbility(
new DiesCreatureTriggeredAbility(
new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)),
false,
StaticFilters.FILTER_CONTROLLED_A_CREATURE),
new ModeChoiceSourceCondition("Legion"),
"&bull Legion &mdash; Whenever a creature you control dies, " +
"put two +1/+1 counters on target creature you control.");
legionAbility.addTarget(new TargetControlledCreaturePermanent());
this.addAbility(legionAbility);
}
private BattleOfHooverDam(final BattleOfHooverDam card) {
super(card);
}
@Override
public BattleOfHooverDam copy() {
return new BattleOfHooverDam(this);
}
}

View file

@ -0,0 +1,49 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.FerociousCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.hint.common.FerociousHint;
import mage.abilities.keyword.PlotAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BeastbondOutcaster extends CardImpl {
public BeastbondOutcaster(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.DRUID);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// When Beastbond Outcaster enters the battlefield, if you control a creature with power 4 or greater, draw a card.
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)),
FerociousCondition.instance, "When {this} enters the battlefield, " +
"if you control a creature with power 4 or greater, draw a card."
).addHint(FerociousHint.instance));
// Plot {1}{G}
this.addAbility(new PlotAbility("{1}{G}"));
}
private BeastbondOutcaster(final BeastbondOutcaster card) {
super(card);
}
@Override
public BeastbondOutcaster copy() {
return new BeastbondOutcaster(this);
}
}

View file

@ -0,0 +1,112 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.PayEnergyCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetNonlandPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
* @author notgreat
*/
public final class BehemothOfVault extends CardImpl {
public BehemothOfVault(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}");
this.subtype.add(SubType.ROBOT);
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// Trample
this.addAbility(TrampleAbility.getInstance());
// When Behemoth of Vault 0 enters the battlefield, you get {E}{E}{E}{E}.
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(4)));
// When Behemoth of Vault 0 dies, you may pay an amount of {E} equal to target nonland permanent's mana value. When you do, destroy that permanent.
Ability ability = new DiesSourceTriggeredAbility(new BehemothOfVaultDoWhenCostPaid(
new ReflexiveTriggeredAbility(new DestroyTargetEffect().setText("destroy that permanent"), false)));
ability.addTarget(new TargetNonlandPermanent());
this.addAbility(ability);
}
private BehemothOfVault(final BehemothOfVault card) {
super(card);
}
@Override
public BehemothOfVault copy() {
return new BehemothOfVault(this);
}
}
//Based on DoWhenCostPaid but constructs the cost itself and uses a fixed target for the effect
class BehemothOfVaultDoWhenCostPaid extends OneShotEffect {
private final ReflexiveTriggeredAbility ability;
BehemothOfVaultDoWhenCostPaid(ReflexiveTriggeredAbility ability) {
super(Outcome.Benefit);
this.ability = ability;
this.staticText = "you may pay an amount of {E} equal to target nonland permanent's mana value. When you do, destroy that permanent.";
}
BehemothOfVaultDoWhenCostPaid(final BehemothOfVaultDoWhenCostPaid effect) {
super(effect);
this.ability = effect.ability.copy();
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent targetPermanent = game.getPermanent(source.getFirstTarget());
if (targetPermanent == null || player == null) {
return false;
}
Cost cost = new PayEnergyCost(targetPermanent.getManaValue());
if (!cost.canPay(source, source, player.getId(), game)
|| !player.chooseUse(Outcome.DestroyPermanent, cost.getText(), source, game)) {
return true;
}
cost.clearPaid();
int bookmark = game.bookmarkState();
if (cost.pay(source, game, source, player.getId(), false)) {
ability.getEffects().setTargetPointer(new FixedTarget(targetPermanent, game));
game.fireReflexiveTriggeredAbility(ability, source);
player.resetStoredBookmark(game);
return true;
}
player.restoreState(bookmark, BehemothOfVaultDoWhenCostPaid.class.getName(), game);
return true;
}
@Override
public void setValue(String key, Object value) {
super.setValue(key, value);
ability.getEffects().setValue(key, value);
}
@Override
public BehemothOfVaultDoWhenCostPaid copy() {
return new BehemothOfVaultDoWhenCostPaid(this);
}
}

View file

@ -2,27 +2,24 @@ package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.common.DealtDamageToSourceTriggeredAbility;
import mage.abilities.dynamicvalue.common.SavedDamageValue;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.DamagedBatchForPermanentsEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.target.common.TargetPlayerOrPlaneswalker;
import java.util.Set;
import java.util.UUID;
/**
* @author Merlingilb
*/
public class BenSolo extends CardImpl {
public BenSolo(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/W}{W}");
this.supertype.add(SuperType.LEGENDARY);
@ -31,11 +28,14 @@ public class BenSolo extends CardImpl {
this.power = new MageInt(4);
this.toughness = new MageInt(4);
//Vigilance
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
//Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker.
this.addAbility(new BenSoloTriggeredAbility());
// Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker.
Ability ability = new DealtDamageToSourceTriggeredAbility(
new DamageTargetEffect(SavedDamageValue.MUCH, "it"), false);
ability.addTarget(new TargetPlayerOrPlaneswalker());
this.addAbility(ability);
}
private BenSolo(final BenSolo card) {
@ -47,103 +47,3 @@ public class BenSolo extends CardImpl {
return new BenSolo(this);
}
}
class BenSoloTriggeredAbility extends TriggeredAbilityImpl {
UUID benSoloID;
BenSoloTriggeredAbility() {
super(Zone.BATTLEFIELD, new BenSoloEffect(), false);
this.addTarget(new TargetPlayerOrPlaneswalker());
benSoloID = this.getSourceId();
}
private BenSoloTriggeredAbility(final BenSoloTriggeredAbility ability) {
super(ability);
benSoloID = ability.benSoloID;
}
@Override
public BenSoloTriggeredAbility copy() {
return new BenSoloTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (benSoloID == null) {
benSoloID = this.getSourceId();
if (benSoloID == null) {
return false;
}
}
int damage = 0;
DamagedBatchForPermanentsEvent dEvent = (DamagedBatchForPermanentsEvent) event;
Set<DamagedEvent> set = dEvent.getEvents();
for (DamagedEvent damagedEvent : set) {
UUID targetID = damagedEvent.getTargetId();
if (targetID == null) {
continue;
}
if (targetID == benSoloID) {
damage += damagedEvent.getAmount();
}
}
if (damage > 0) {
this.getEffects().setValue("damage", damage);
this.getEffects().setValue("benSoloID", benSoloID);
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever Ben Solo is dealt damage, it deals that much damage to target player or planeswalker.";
}
}
class BenSoloEffect extends OneShotEffect {
BenSoloEffect() {
super(Outcome.Benefit);
}
private BenSoloEffect(final BenSoloEffect effect) {
super(effect);
}
@Override
public BenSoloEffect copy() {
return new BenSoloEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Integer damage = (Integer)getValue("damage");
UUID benSoloID = (UUID)getValue("benSoloID");
if (benSoloID == null || damage == null || damage < 1) {
return false;
}
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) {
permanent.damage(damage, benSoloID, source, game);
return true;
}
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
player.damage(damage, benSoloID, source, game);
return true;
}
return false;
}
}

View file

@ -0,0 +1,84 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.other.AnotherTargetPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BetrayalAtTheVault extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other target creatures");
static {
filter.add(new AnotherTargetPredicate(2));
}
public BetrayalAtTheVault(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{G}{G}");
// Target creature you control deals damage equal to its power to each of two other target creatures.
this.getSpellAbility().addEffect(new BetrayalAtTheVaultEffect());
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent().setTargetTag(1));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(2, 2, filter, false).setTargetTag(2));
}
private BetrayalAtTheVault(final BetrayalAtTheVault card) {
super(card);
}
@Override
public BetrayalAtTheVault copy() {
return new BetrayalAtTheVault(this);
}
}
class BetrayalAtTheVaultEffect extends OneShotEffect {
BetrayalAtTheVaultEffect() {
super(Outcome.Benefit);
staticText = "Target creature you control deals damage equal to its power "
+ "to each of two other target creatures";
}
private BetrayalAtTheVaultEffect(final BetrayalAtTheVaultEffect effect) {
super(effect);
}
@Override
public BetrayalAtTheVaultEffect copy() {
return new BetrayalAtTheVaultEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanent(source.getFirstTarget());
if (creature == null) {
return false;
}
source.getTargets()
.stream()
.filter(t -> t.getTargetTag() == 2)
.map(Target::getTargets)
.flatMap(List::stream)
.map(game::getPermanent)
.filter(Objects::nonNull)
.forEach(p -> p.damage(creature.getPower().getValue(), creature.getId(), source, game));
return true;
}
}

View file

@ -0,0 +1,106 @@
package mage.cards.b;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.effects.common.continuous.NextSpellCastHasAbilityEffect;
import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.keyword.CascadeAbility;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.TargetPlayer;
import java.util.UUID;
/**
*
* @author notgreat
*/
public final class BiggerOnTheInside extends CardImpl {
private static final FilterPermanent filter
= new FilterPermanent("artifact or land");
static {
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.LAND.getPredicate()
));
}
public BiggerOnTheInside(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{G}");
this.subtype.add(SubType.AURA);
// Enchant artifact or land
TargetPermanent auraTarget = new TargetPermanent(filter);
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
this.addAbility(new EnchantAbility(auraTarget));
// Enchanted permanent has "{T}: Target player adds two mana of any one color. The next spell they cast this turn has cascade."
Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BiggerOnTheInsideEffect(), new TapSourceCost());
gainedAbility.addTarget(new TargetPlayer());
Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA, Duration.WhileOnBattlefield, null, "permanent");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
}
private BiggerOnTheInside(final BiggerOnTheInside card) {
super(card);
}
@Override
public BiggerOnTheInside copy() {
return new BiggerOnTheInside(this);
}
}
class BiggerOnTheInsideEffect extends OneShotEffect { //Not a mana ability since it targets
BiggerOnTheInsideEffect() {
super(Outcome.Benefit);
staticText = "Target player adds two mana of any one color. The next spell they cast this turn has cascade";
}
private BiggerOnTheInsideEffect(final BiggerOnTheInsideEffect effect) {
super(effect);
}
@Override
public BiggerOnTheInsideEffect copy() {
return new BiggerOnTheInsideEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player == null) {
return false;
}
ContinuousEffect cascadeEffect = new NextSpellCastHasAbilityEffect(new CascadeAbility(), StaticFilters.FILTER_CARD, TargetController.SOURCE_TARGETS);
game.addEffect(cascadeEffect, source);
ManaEffect manaEffect = new AddManaOfAnyColorEffect(2);
Mana manaToAdd = manaEffect.produceMana(game, source);
if (manaToAdd != null && manaToAdd.count() > 0) {
player.getManaPool().addMana(manaToAdd, game, source);
}
return true;
}
}

View file

@ -0,0 +1,57 @@
package mage.cards.b;
import mage.MageInt;
import mage.Mana;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue;
import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.keyword.VigilanceAbility;
import mage.abilities.mana.DynamicManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import java.util.UUID;
/**
* @author notgreat
*/
public final class BighornerRancher extends CardImpl {
public BighornerRancher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.RANGER);
this.power = new MageInt(2);
this.toughness = new MageInt(5);
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// {T}: Add an amount of {G} equal to the greatest power among creatures you control.
this.addAbility(new DynamicManaAbility(
Mana.GreenMana(1), GreatestPowerAmongControlledCreaturesValue.instance, new TapSourceCost(),
"Add an amount of {G} equal to the greatest power among creatures you control."
));
// Sacrifice Bighorner Rancher: You gain life equal to the greatest toughness among other creatures you control.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.instance).setText("You gain life equal to the greatest toughness among other creatures you control."),
new SacrificeSourceCost()));
}
private BighornerRancher(final BighornerRancher card) {
super(card);
}
@Override
public BighornerRancher copy() {
return new BighornerRancher(this);
}
}

View file

@ -0,0 +1,106 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.*;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.FaceDownPredicate;
import mage.filter.predicate.card.OwnerIdPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInExile;
import mage.target.common.TargetOpponent;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author Susucr
*/
public final class BindingNegotiation extends CardImpl {
public BindingNegotiation(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
// Target opponent reveals their hand. You may choose a nonland card from it. If you do, they discard it. Otherwise, you may put a face-up exiled card they own into their graveyard.
this.getSpellAbility().addEffect(new BindingNegotiationEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
private BindingNegotiation(final BindingNegotiation card) {
super(card);
}
@Override
public BindingNegotiation copy() {
return new BindingNegotiation(this);
}
}
class BindingNegotiationEffect extends OneShotEffect {
BindingNegotiationEffect() {
super(Outcome.Discard);
staticText = "Target opponent reveals their hand. You may choose a nonland card from it. "
+ "If you do, they discard it. Otherwise, you may put a face-up exiled card they own into their graveyard";
}
private BindingNegotiationEffect(final BindingNegotiationEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
Player controller = game.getPlayer(source.getControllerId());
if (player == null || controller == null) {
return false;
}
// Target opponent reveals their hand
Cards revealedCards = new CardsImpl();
revealedCards.addAll(player.getHand());
player.revealCards(source, revealedCards, game);
// You may choose a nonland card from it.
TargetCard target = new TargetCard(0, 1, Zone.HAND, new FilterNonlandCard());
controller.choose(outcome, revealedCards, target, source, game);
UUID chosenId = target.getFirstTarget();
if (chosenId != null) {
// If you do, they discard it.
Card card = revealedCards.get(target.getFirstTarget(), game);
player.discard(card, false, source, game);
} else {
// Otherwise, you may put a face-up exiled card they own into their graveyard.
FilterCard filter = new FilterCard("face-up exiled card owned by " + player.getName());
filter.add(Predicates.not(FaceDownPredicate.instance));
filter.add(new OwnerIdPredicate(player.getId()));
TargetCard targetExiled = new TargetCardInExile(0, 1, filter, null);
controller.choose(outcome, targetExiled, source, game);
Set<Card> chosenExiledCard = targetExiled
.getTargets()
.stream()
.map(game::getCard)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (!chosenExiledCard.isEmpty()) {
player.moveCards(chosenExiledCard, Zone.GRAVEYARD, source, game);
}
}
return true;
}
@Override
public BindingNegotiationEffect copy() {
return new BindingNegotiationEffect(this);
}
}

View file

@ -0,0 +1,69 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.condition.common.CollectedEvidenceCondition;
import mage.abilities.costs.CostAdjuster;
import mage.abilities.costs.OptionalAdditionalCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.keyword.CollectEvidenceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author notgreat
*/
public final class BiteDownOnCrime extends CardImpl {
public BiteDownOnCrime(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}");
// As an additional cost to cast this spell, you may collect evidence 6. This spell costs {2} less to cast if evidence was collected.
this.addAbility(new CollectEvidenceAbility(6,"This spell costs {2} less to cast if evidence was collected"));
this.getSpellAbility().setCostAdjuster(BiteDownOnCrimeAdjuster.instance);
// Target creature you control gets +2/+0 until end of turn.
Effect effect = new BoostTargetEffect(2, 0, Duration.EndOfTurn);
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
// It deals damage equal to its power to target creature you don't control.
this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("It"));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
}
private BiteDownOnCrime(final BiteDownOnCrime card) {
super(card);
}
@Override
public BiteDownOnCrime copy() {
return new BiteDownOnCrime(this);
}
}
enum BiteDownOnCrimeAdjuster implements CostAdjuster {
instance;
private static final OptionalAdditionalCost collectEvidenceCost = CollectEvidenceAbility.makeCost(6);
@Override
public void adjustCosts(Ability ability, Game game) {
if (CollectedEvidenceCondition.instance.apply(game, ability)
|| (game.inCheckPlayableState() && collectEvidenceCost.canPay(ability, null, ability.getControllerId(), game))) {
CardUtil.reduceCost(ability, 2);
}
}
}

View file

@ -0,0 +1,51 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.condition.common.MorbidCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.hint.common.MorbidHint;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.PlotAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BlacksnagBuzzard extends CardImpl {
public BlacksnagBuzzard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.BIRD);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Blacksnag Buzzard enters the battlefield with a +1/+1 counter on it if a creature died this turn.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(
new AddCountersSourceEffect(CounterType.P1P1.createInstance()), MorbidCondition.instance, ""
), "with a +1/+1 counter on it if a creature died this turn").addHint(MorbidHint.instance));
// Plot {1}{B}
this.addAbility(new PlotAbility("{1}{B}"));
}
private BlacksnagBuzzard(final BlacksnagBuzzard card) {
super(card);
}
@Override
public BlacksnagBuzzard copy() {
return new BlacksnagBuzzard(this);
}
}

View file

@ -1,18 +1,12 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetadjustment.EachOpponentPermanentTargetsAdjuster;
import mage.target.targetpointer.EachTargetPointer;
import java.util.UUID;
@ -29,7 +23,7 @@ public final class BlatantThievery extends CardImpl {
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.Custom, true)
.setTargetPointer(new EachTargetPointer())
.setText("for each opponent, gain control of target permanent that player controls"));
this.getSpellAbility().setTargetAdjuster(BlatantThieveryAdjuster.instance);
this.getSpellAbility().setTargetAdjuster(new EachOpponentPermanentTargetsAdjuster(new TargetPermanent()));
}
private BlatantThievery(final BlatantThievery card) {
@ -41,25 +35,3 @@ public final class BlatantThievery extends CardImpl {
return new BlatantThievery(this);
}
}
enum BlatantThieveryAdjuster implements TargetAdjuster {
instance;
@Override
public void adjustTargets(Ability ability, Game game) {
ability.getTargets().clear();
for (UUID opponentId : game.getOpponents(ability.getControllerId())) {
Player opponent = game.getPlayer(opponentId);
if (opponent == null || game.getBattlefield().count(
StaticFilters.FILTER_CONTROLLED_PERMANENT,
opponentId, ability, game
) < 1) {
continue;
}
FilterPermanent filter = new FilterPermanent("Permanent controlled by " + opponent.getName());
filter.add(new ControllerIdPredicate(opponentId));
TargetPermanent targetPermanent = new TargetPermanent(filter);
ability.addTarget(targetPermanent);
}
}
}

View file

@ -67,7 +67,7 @@ class BloodHoundTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
}
@Override

View file

@ -0,0 +1,53 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.CommittedCrimeTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BloodHustler extends CardImpl {
public BloodHustler(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Whenever you commit a crime, put a +1/+1 counter on Blood Hustler. This ability triggers only once each turn.
this.addAbility(new CommittedCrimeTriggeredAbility(
new AddCountersSourceEffect(CounterType.P1P1.createInstance())
).setTriggersOnceEachTurn(true));
// {3}{B}: Target opponent loses 1 life and you gain 1 life.
Ability ability = new SimpleActivatedAbility(new LoseLifeTargetEffect(1), new ManaCostsImpl<>("{3}{B}"));
ability.addEffect(new GainLifeEffect(1).concatBy("and"));
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
}
private BloodHustler(final BloodHustler card) {
super(card);
}
@Override
public BloodHustler copy() {
return new BloodHustler(this);
}
}

View file

@ -85,9 +85,7 @@ class BloodSpatterAnalysisTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeBatchEvent zBatchEvent = (ZoneChangeBatchEvent) event;
for (ZoneChangeEvent zEvent : zBatchEvent.getEvents()) {
for (ZoneChangeEvent zEvent : ((ZoneChangeBatchEvent) event).getEvents()) {
if (zEvent.isDiesEvent()) {
Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId());
if (permanent != null && permanent.isCreature(game)) {

View file

@ -0,0 +1,104 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.MenaceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.predicate.mageobject.OutlawPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TreasureToken;
import java.util.List;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BoneyardDesecrator extends CardImpl {
public BoneyardDesecrator(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.MERCENARY);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Menace
this.addAbility(new MenaceAbility(false));
// {1}{B}, Sacrifice another creature: Put a +1/+1 counter on Boneyard Desecrator. If an outlaw was sacrificed this way, create a Treasure token.
Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance());
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl<>("{1}{B}"));
ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE));
ability.addEffect(new BoneyardDesecratorEffect());
this.addAbility(ability);
}
private BoneyardDesecrator(final BoneyardDesecrator card) {
super(card);
}
@Override
public BoneyardDesecrator copy() {
return new BoneyardDesecrator(this);
}
}
// Inspired by Thallid Omnivore
class BoneyardDesecratorEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("an outlaw");
static {
filter.add(OutlawPredicate.instance);
}
BoneyardDesecratorEffect() {
super(Outcome.GainLife);
this.staticText = "If an outlaw was sacrificed this way, create a Treasure token";
}
private BoneyardDesecratorEffect(final BoneyardDesecratorEffect effect) {
super(effect);
}
@Override
public BoneyardDesecratorEffect copy() {
return new BoneyardDesecratorEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
for (Cost cost : source.getCosts()) {
if (cost instanceof SacrificeTargetCost) {
SacrificeTargetCost sacrificeCost = (SacrificeTargetCost) cost;
List<Permanent> permanents = sacrificeCost.getPermanents();
for (Permanent permanent : permanents) {
if (!filter.match(permanent, source.getControllerId(), source, game)) {
continue;
}
return new CreateTokenEffect(new TreasureToken()).apply(game, source);
}
}
}
return false;
}
}

View file

@ -0,0 +1,55 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.PutCardFromHandOrGraveyardOntoBattlefieldEffect;
import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.StaticFilters;
import mage.game.permanent.token.BeauToken;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BonnyPallClearcutter extends CardImpl {
public BonnyPallClearcutter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{U}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.GIANT);
this.subtype.add(SubType.SCOUT);
this.power = new MageInt(6);
this.toughness = new MageInt(5);
// Reach
this.addAbility(ReachAbility.getInstance());
// When Bonny Pall, Clearcutter enters the battlefield, create Beau, a legendary blue Ox creature token with "This creature's power and toughness are each equal to the number of lands you control."
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BeauToken())));
// Whenever you attack, draw a card, then you may put a land card from your hand or graveyard onto the battlefield.
Ability ability = new AttacksWithCreaturesTriggeredAbility(new DrawCardSourceControllerEffect(1), 1);
ability.addEffect(new PutCardFromHandOrGraveyardOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A, false).concatBy(", then"));
this.addAbility(ability);
}
private BonnyPallClearcutter(final BonnyPallClearcutter card) {
super(card);
}
@Override
public BonnyPallClearcutter copy() {
return new BonnyPallClearcutter(this);
}
}

View file

@ -0,0 +1,50 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetArtifactPermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetLandPermanent;
import mage.target.targetpointer.EachTargetPointer;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BoomBox extends CardImpl {
public BoomBox(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
// {6}, {T}, Sacrifice Boom Box: Destroy up to one target artifact, up to one target creature, and up to one target land.
Ability ability = new SimpleActivatedAbility(
new DestroyTargetEffect("")
.setText("Destroy up to one target artifact, up to one target creature, and up to one target land")
.setTargetPointer(new EachTargetPointer()),
new GenericManaCost(6)
);
ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetArtifactPermanent(0, 1));
ability.addTarget(new TargetCreaturePermanent(0, 1));
ability.addTarget(new TargetLandPermanent(0, 1));
this.addAbility(ability);
}
private BoomBox(final BoomBox card) {
super(card);
}
@Override
public BoomBox copy() {
return new BoomBox(this);
}
}

View file

@ -0,0 +1,84 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.ImproviseAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.TreasureToken;
import mage.players.Player;
import mage.target.common.TargetAnyTarget;
import java.util.UUID;
/**
* @author notgreat
*/
public final class BottleCapBlast extends CardImpl {
public BottleCapBlast(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}");
// Improvise
this.addAbility(new ImproviseAbility());
// Bottle-Cap Blast deals 5 damage to any target. If excess damage was dealt to a permanent this way, create that many tapped Treasure tokens.
this.getSpellAbility().addEffect(new BottleCapBlastEffect());
this.getSpellAbility().addTarget(new TargetAnyTarget());
}
private BottleCapBlast(final BottleCapBlast card) {
super(card);
}
@Override
public BottleCapBlast copy() {
return new BottleCapBlast(this);
}
}
//Based on Hell to Pay
class BottleCapBlastEffect extends OneShotEffect {
BottleCapBlastEffect() {
super(Outcome.Benefit);
staticText = "{this} deals 5 damage to any target. " +
"If excess damage was dealt to a permanent this way, create that many tapped Treasure tokens.";
}
private BottleCapBlastEffect(final BottleCapBlastEffect effect) {
super(effect);
}
@Override
public BottleCapBlastEffect copy() {
return new BottleCapBlastEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
UUID target = getTargetPointer().getFirst(game, source);
Player player = game.getPlayer(target);
if (player != null) {
player.damage(5, source, game);
return true;
}
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent == null) {
return false;
}
int lethal = Math.min(permanent.getLethalDamage(source.getSourceId(), game), 5);
permanent.damage(5, source.getSourceId(), source, game);
if (lethal < 5) {
new TreasureToken().putOntoBattlefield(
5 - lethal, game, source, source.getControllerId(), true, false
);
}
return true;
}
}

View file

@ -0,0 +1,65 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksWhileSaddledTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.SaddleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BoundingFelidar extends CardImpl {
private final static FilterPermanent filter = new FilterControlledCreaturePermanent("other creature you control");
static {
filter.add(AnotherPredicate.instance);
}
private final static DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
private final static Hint hint = new ValueHint("Other creatures you control", xValue);
public BoundingFelidar(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
this.subtype.add(SubType.CAT);
this.subtype.add(SubType.BEAST);
this.subtype.add(SubType.MOUNT);
this.power = new MageInt(4);
this.toughness = new MageInt(7);
// Whenever Bounding Felidar attacks while saddled, put a +1/+1 counter on each other creature you control. You gain 1 life for each of those creatures.
Ability ability = new AttacksWhileSaddledTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter));
ability.addEffect(new GainLifeEffect(xValue).setText("You gain 1 life for each of those creatures"));
ability.addHint(hint);
this.addAbility(ability);
// Saddle 2
this.addAbility(new SaddleAbility(2));
}
private BoundingFelidar(final BoundingFelidar card) {
super(card);
}
@Override
public BoundingFelidar copy() {
return new BoundingFelidar(this);
}
}

View file

@ -0,0 +1,46 @@
package mage.cards.b;
import mage.abilities.effects.common.CreateTokenControllerTargetPermanentEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.game.permanent.token.Ox22Token;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BovineIntervention extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifact or creature");
static {
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate())
);
}
public BovineIntervention(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
// Destroy target artifact or creature. Its controller creates a 2/2 white Ox creature token.
this.getSpellAbility().addTarget(new TargetPermanent(filter));
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addEffect(new CreateTokenControllerTargetPermanentEffect(new Ox22Token()));
}
private BovineIntervention(final BovineIntervention card) {
super(card);
}
@Override
public BovineIntervention copy() {
return new BovineIntervention(this);
}
}

View file

@ -0,0 +1,111 @@
package mage.cards.b;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.PutCards;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author DominionSpy
*/
public final class BreakOut extends CardImpl {
public BreakOut(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{G}");
// Look at the top six cards of your library. You may reveal a creature card from among them.
// If that card has mana value 2 or less, you may put it onto the battlefield and it gains haste until end of turn.
// If you didn't put the revealed card onto the battlefield this way, put it into your hand.
// Put the rest on the bottom of your library in a random order.
this.getSpellAbility().addEffect(new BreakOutEffect());
}
private BreakOut(final BreakOut card) {
super(card);
}
@Override
public BreakOut copy() {
return new BreakOut(this);
}
}
class BreakOutEffect extends OneShotEffect {
BreakOutEffect() {
super(Outcome.Benefit);
staticText = "Look at the top six cards of your library. You may reveal a creature card from among them. " +
"If that card has mana value 2 or less, you may put it onto the battlefield and it gains haste until end of turn. " +
"If you didn't put the revealed card onto the battlefield this way, put it into your hand. " +
"Put the rest on the bottom of your library in a random order.";
}
private BreakOutEffect(final BreakOutEffect effect) {
super(effect);
}
@Override
public BreakOutEffect copy() {
return new BreakOutEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 6));
controller.lookAtCards(source, null, cards, game);
TargetCard target = new TargetCard(0, 1, Zone.LIBRARY, StaticFilters.FILTER_CARD_CREATURE);
target.withChooseHint("Reveal a creature card?");
if (!controller.chooseTarget(outcome, cards, target, source, game)) {
return PutCards.BOTTOM_RANDOM.moveCards(controller, cards, source, game);
}
Card pickedCard = game.getCard(target.getFirstTarget());
if (pickedCard != null) {
controller.revealCards(source, new CardsImpl(pickedCard), game);
cards.remove(pickedCard);
if (pickedCard.getManaValue() <= 2 &&
controller.chooseUse(Outcome.PutCardInPlay, "Put it onto the battlefield?", source, game) &&
controller.moveCards(pickedCard, Zone.BATTLEFIELD, source, game)) {
Permanent permanent = game.getPermanent(pickedCard.getId());
if (permanent != null) {
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(effect, source);
}
} else {
controller.moveCards(pickedCard, Zone.HAND, source, game);
}
}
PutCards.BOTTOM_RANDOM.moveCards(controller, cards, source, game);
return true;
}
}

View file

@ -9,8 +9,8 @@ import mage.abilities.keyword.PartnerAbility;
import mage.cards.*;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.DamagedBatchForPlayersEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -72,9 +72,8 @@ class BreechesBrazenPlundererTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
DamagedBatchForPlayersEvent dEvent = (DamagedBatchForPlayersEvent) event;
Set<UUID> opponents = new HashSet<>();
for (DamagedEvent damagedEvent : dEvent.getEvents()) {
for (DamagedEvent damagedEvent : ((DamagedBatchForPlayersEvent) event).getEvents()) {
Permanent permanent = game.getPermanent(damagedEvent.getSourceId());
if (permanent == null
|| !permanent.isControlledBy(getControllerId())
@ -84,7 +83,7 @@ class BreechesBrazenPlundererTriggeredAbility extends TriggeredAbilityImpl {
}
opponents.add(damagedEvent.getTargetId());
}
if (opponents.size() < 1) {
if (opponents.isEmpty()) {
return false;
}
this.getEffects().clear();
@ -148,4 +147,4 @@ class BreechesBrazenPlundererEffect extends OneShotEffect {
}
return true;
}
}
}

View file

@ -0,0 +1,103 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.CastSecondSpellTriggeredAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyStackObjectEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.keyword.MenaceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.common.TargetAnyTarget;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BreechesTheBlastmaker extends CardImpl {
public BreechesTheBlastmaker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.PIRATE);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Menace
this.addAbility(new MenaceAbility(false));
// Whenever you cast your second spell each turn, you may sacrifice an artifact. If you do, flip a coin. When you win the flip, copy that spell. You may choose new targets for the copy. When you lose the flip, Breeches, the Blastmaker deals damage equal to that spell's mana value to any target.
this.addAbility(new CastSecondSpellTriggeredAbility(new DoIfCostPaid(
new BreechesTheBlastmakerEffect(), new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_ARTIFACT_AN)
)));
}
private BreechesTheBlastmaker(final BreechesTheBlastmaker card) {
super(card);
}
@Override
public BreechesTheBlastmaker copy() {
return new BreechesTheBlastmaker(this);
}
}
class BreechesTheBlastmakerEffect extends OneShotEffect {
BreechesTheBlastmakerEffect() {
super(Outcome.Benefit);
staticText = "flip a coin. When you win the flip, copy that spell. You may choose new targets for the copy. " +
"When you lose the flip, {this} deals damage equal to that spell's mana value to any target";
}
private BreechesTheBlastmakerEffect(final BreechesTheBlastmakerEffect effect) {
super(effect);
}
@Override
public BreechesTheBlastmakerEffect copy() {
return new BreechesTheBlastmakerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Spell spell = (Spell) getValue("spellCast");
ReflexiveTriggeredAbility ability;
if (player.flipCoin(source, game, true)) {
Effect effect = new CopyStackObjectEffect();
effect.setText("copy that spell. You may choose new targets for the copy");
effect.setValue("stackObject", spell);
ability = new ReflexiveTriggeredAbility(effect, false);
} else {
int mv = Optional
.ofNullable(spell)
.map(Spell::getManaValue)
.orElse(0);
ability = new ReflexiveTriggeredAbility(new DamageTargetEffect(mv), false);
ability.addTarget(new TargetAnyTarget());
}
game.fireReflexiveTriggeredAbility(ability, source);
return true;
}
}

View file

@ -0,0 +1,47 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.common.AttacksWhileSaddledTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.SaddleAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.game.permanent.token.SheepWhiteToken;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BridledBighorn extends CardImpl {
public BridledBighorn(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.SHEEP);
this.subtype.add(SubType.MOUNT);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// Whenever Bridled Bighorn attacks while saddled, create a 1/1 white Sheep creature token.
this.addAbility(new AttacksWhileSaddledTriggeredAbility(new CreateTokenEffect(new SheepWhiteToken())));
// Saddle 2
this.addAbility(new SaddleAbility(2));
}
private BridledBighorn(final BridledBighorn card) {
super(card);
}
@Override
public BridledBighorn copy() {
return new BridledBighorn(this);
}
}

View file

@ -0,0 +1,36 @@
package mage.cards.b;
import mage.abilities.common.CastSecondSpellTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.PlotAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.MercenaryToken;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BrimstoneRoundup extends CardImpl {
public BrimstoneRoundup(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}");
// Whenever you cast your second spell each turn, create a 1/1 red Mercenary creature token with "{T}: Target creature you control gets +1/+0 until end of turn. Activate only as a sorcery."
this.addAbility(new CastSecondSpellTriggeredAbility(new CreateTokenEffect(new MercenaryToken())));
// Plot {2}{R}
this.addAbility(new PlotAbility("{2}{R}"));
}
private BrimstoneRoundup(final BrimstoneRoundup card) {
super(card);
}
@Override
public BrimstoneRoundup copy() {
return new BrimstoneRoundup(this);
}
}

View file

@ -0,0 +1,54 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.MillThenPutInHandEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.game.permanent.token.FoodToken;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BristlebudFarmer extends CardImpl {
public BristlebudFarmer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.DRUID);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Trample
this.addAbility(TrampleAbility.getInstance());
// When Bristlebud Farmer enters the battlefield, create two Food tokens.
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FoodToken(), 2)));
// Whenever Bristlebud Farmer attacks, you may sacrifice a Food. If you do, mill three cards. You may put a permanent card from among them into your hand.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(
new MillThenPutInHandEffect(3, StaticFilters.FILTER_CARD_A_PERMANENT).withTextOptions("them"),
new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_FOOD)
)));
}
private BristlebudFarmer(final BristlebudFarmer card) {
super(card);
}
@Override
public BristlebudFarmer copy() {
return new BristlebudFarmer(this);
}
}

View file

@ -0,0 +1,47 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.FerociousCondition;
import mage.abilities.decorator.ConditionalAsThoughEffect;
import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect;
import mage.abilities.keyword.DefenderAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BristlepackSentry extends CardImpl {
public BristlepackSentry(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.WOLF);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Defender
this.addAbility(DefenderAbility.getInstance());
// As long as you control a creature with power 4 or greater, Bristlepack Sentry can attack as though it didn't have defender.
this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect(
new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), FerociousCondition.instance
).setText("as long as you control a creature with power 4 or greater, {this} can attack as though it didn't have defender")));
}
private BristlepackSentry(final BristlepackSentry card) {
super(card);
}
@Override
public BristlepackSentry copy() {
return new BristlepackSentry(this);
}
}

View file

@ -0,0 +1,48 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.mana.GreenManaAbility;
import mage.abilities.mana.RedManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BristlingBackwoods extends CardImpl {
public BristlingBackwoods(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.subtype.add(SubType.DESERT);
// Bristling Backwoods enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility());
// When Bristling Backwoods enters the battlefield, it deals 1 damage to target opponent.
Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "it"));
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
// {T}: Add {R} or {G}.
this.addAbility(new RedManaAbility());
this.addAbility(new GreenManaAbility());
}
private BristlingBackwoods(final BristlingBackwoods card) {
super(card);
}
@Override
public BristlingBackwoods copy() {
return new BristlingBackwoods(this);
}
}

View file

@ -0,0 +1,54 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LandfallAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.effects.common.counter.DoubleCounterOnEachPermanentEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class BristlyBillSpineSower extends CardImpl {
public BristlyBillSpineSower(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.DRUID);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Landfall -- Whenever a land enters the battlefield under your control, put a +1/+1 counter on target creature.
Ability ability = new LandfallAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
// {3}{G}{G}: Double the number of +1/+1 counters on each creature you control.
this.addAbility(new SimpleActivatedAbility(new DoubleCounterOnEachPermanentEffect(
CounterType.P1P1, StaticFilters.FILTER_CONTROLLED_CREATURE
), new ManaCostsImpl<>("{3}{G}{G}")));
}
private BristlyBillSpineSower(final BristlyBillSpineSower card) {
super(card);
}
@Override
public BristlyBillSpineSower copy() {
return new BristlyBillSpineSower(this);
}
}

View file

@ -1,6 +1,5 @@
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -11,22 +10,22 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.Card;
import mage.constants.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetCardInExile;
import mage.target.common.TargetNonlandPermanent;
import mage.target.targetadjustment.EachOpponentPermanentTargetsAdjuster;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.EachTargetPointer;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author jimga150
@ -47,7 +46,7 @@ public final class BronzebeakForagers extends CardImpl {
.setTargetPointer(new EachTargetPointer())
.setText("for each opponent, exile up to one target nonland permanent that player controls until {this} leaves the battlefield")
);
etbAbility.setTargetAdjuster(BronzebeakForagerExileAdjuster.instance);
etbAbility.setTargetAdjuster(new EachOpponentPermanentTargetsAdjuster(new TargetNonlandPermanent(0, 1)));
this.addAbility(etbAbility);
// {X}{W}: Put target card with mana value X exiled with Bronzebeak Foragers into its owner's graveyard.
@ -72,26 +71,6 @@ public final class BronzebeakForagers extends CardImpl {
}
}
enum BronzebeakForagerExileAdjuster implements TargetAdjuster {
instance;
@Override
public void adjustTargets(Ability ability, Game game) {
ability.getTargets().clear();
for (UUID opponentId : game.getOpponents(ability.getControllerId())) {
Player opponent = game.getPlayer(opponentId);
if (opponent == null) {
continue;
}
FilterPermanent filter = new FilterPermanent("nonland permanent controlled by " + opponent.getLogName());
filter.add(new ControllerIdPredicate(opponentId));
filter.add(Predicates.not(CardType.LAND.getPredicate()));
TargetPermanent target = new TargetPermanent(0, 1, filter, false);
ability.addTarget(target);
}
}
}
// Based on Gelatinous Cube
enum BronzebeakForagerDissolveAdjuster implements TargetAdjuster {
instance;

View file

@ -0,0 +1,48 @@
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect;
import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.CrewAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent;
/**
*
* @author justinjohnson14
*/
public final class BrotherhoodVertibird extends CardImpl {
public BrotherhoodVertibird(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(0);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Brotherhood Vertibird's power is equal to the number of artifacts you control.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerSourceEffect(ArtifactYouControlCount.instance)));
// Crew 2
this.addAbility(new CrewAbility(2));
}
private BrotherhoodVertibird(final BrotherhoodVertibird card) {
super(card);
}
@Override
public BrotherhoodVertibird copy() {
return new BrotherhoodVertibird(this);
}
}

View file

@ -0,0 +1,108 @@
package mage.cards.b;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.DoubleStrikeAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.token.Ox22Token;
import mage.players.Library;
import mage.players.Player;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BruseTarlRovingRancher extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent(SubType.OX, "Oxen");
public BruseTarlRovingRancher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(4);
this.toughness = new MageInt(3);
// Oxen you control have double strike.
this.addAbility(new SimpleStaticAbility(
new GainAbilityControlledEffect(
DoubleStrikeAbility.getInstance(),
Duration.WhileOnBattlefield, filter
)
));
// Whenever Bruse Tarl, Roving Rancher enters the battlefield or attacks, exile the top card of your library. If it's a land card, create a 2/2 white Ox creature token. Otherwise, you may cast it until the end of your next turn.
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new BruseTarlRovingRancherEffect()));
}
private BruseTarlRovingRancher(final BruseTarlRovingRancher card) {
super(card);
}
@Override
public BruseTarlRovingRancher copy() {
return new BruseTarlRovingRancher(this);
}
}
class BruseTarlRovingRancherEffect extends OneShotEffect {
BruseTarlRovingRancherEffect() {
super(Outcome.Benefit);
staticText = "exile the top card of your library. "
+ "If it's a land card, create a 2/2 white Ox creature token. "
+ "Otherwise, you may cast it until the end of your next turn.";
}
private BruseTarlRovingRancherEffect(final BruseTarlRovingRancherEffect effect) {
super(effect);
}
@Override
public BruseTarlRovingRancherEffect copy() {
return new BruseTarlRovingRancherEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Library library = player.getLibrary();
if (library == null || !library.hasCards()) {
return false;
}
Card card = library.getFromTop(game);
if (card == null) {
return false;
}
if (card.isLand(game)) {
player.moveCards(card, Zone.EXILED, source, game);
new CreateTokenEffect(new Ox22Token()).apply(game, source);
} else {
PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(
game, source, card, TargetController.YOU,
Duration.UntilEndOfYourNextTurn,
false, false, true
);
}
return true;
}
}

View file

@ -0,0 +1,52 @@
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.AsTurnedFaceUpEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.constants.SubType;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.keyword.DisguiseAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.counters.CounterType;
/**
*
* @author notgreat
*/
public final class BubbleSmuggler extends CardImpl {
public BubbleSmuggler(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
this.subtype.add(SubType.OCTOPUS);
this.subtype.add(SubType.FISH);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
// Disguise {5}{U}
this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{5}{U}")));
// As Bubble Smuggler is turned face up, put four +1/+1 counters on it.
Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance(4));
effect.setText("put four +1/+1 counters on it");
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new AsTurnedFaceUpEffect(effect, false));
ability.setWorksFaceDown(true);
this.addAbility(ability);
}
private BubbleSmuggler(final BubbleSmuggler card) {
super(card);
}
@Override
public BubbleSmuggler copy() {
return new BubbleSmuggler(this);
}
}

View file

@ -0,0 +1,115 @@
package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.mana.ColorlessManaAbility;
import mage.abilities.mana.ConditionalAnyColorManaAbility;
import mage.abilities.mana.conditional.ConditionalSpellManaBuilder;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
* @author Susucr
*/
public final class BucolicRanch extends CardImpl {
private static final FilterSpell filter = new FilterSpell("a Mount spell");
static {
filter.add(SubType.MOUNT.getPredicate());
}
public BucolicRanch(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.subtype.add(SubType.DESERT);
// {T}: Add {C}.
this.addAbility(new ColorlessManaAbility());
// {T}: Add one mana of any color. Spend this mana only to cast a Mount spell.
this.addAbility(new ConditionalAnyColorManaAbility(
new TapSourceCost(), 1,
new ConditionalSpellManaBuilder(filter), true
));
// {3}, {T}: Look at the top card of your library. If it's a Mount card, you may reveal it and put it into your hand. If you don't put it into your hand, you may put it on the bottom of your library.
Ability ability = new SimpleActivatedAbility(new BucolicRanchEffect(), new GenericManaCost(3));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}
private BucolicRanch(final BucolicRanch card) {
super(card);
}
@Override
public BucolicRanch copy() {
return new BucolicRanch(this);
}
}
class BucolicRanchEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("a Mount card");
static {
filter.add(SubType.MOUNT.getPredicate());
}
BucolicRanchEffect() {
super(Outcome.Benefit);
staticText = "look at the top card of your library. If it's a Mount card, " +
"you may reveal it and put it into your hand. If you don't put it " +
"into your hand, you may put it on the bottom of your library";
}
private BucolicRanchEffect(final BucolicRanchEffect effect) {
super(effect);
}
@Override
public BucolicRanchEffect copy() {
return new BucolicRanchEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
Card card = player.getLibrary().getFromTop(game);
if (card == null) {
return false;
}
player.lookAtCards("Top card of library", card, game);
if (filter.match(card, player.getId(), source, game) && player.chooseUse(
outcome, "Put " + card.getName() + " into your hand?", source, game
)) {
player.revealCards(source, new CardsImpl(card), game);
player.moveCards(card, Zone.HAND, source, game);
}
if (Zone.LIBRARY.equals(game.getState().getZone(card.getId())) && player.chooseUse(
outcome, "Put " + card.getName() + " on the bottom of your library?", source, game
)) {
player.putCardsOnBottomOfLibrary(card, game, source, false);
}
return true;
}
}

View file

@ -0,0 +1,69 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.common.BecomesTargetSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.abilities.keyword.ReachAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import java.util.UUID;
/**
* @author Susucr
*/
public final class Cactarantula extends CardImpl {
private static final Condition condition = new PermanentsOnTheBattlefieldCondition(
new FilterPermanent(SubType.DESERT, "you control a Desert")
);
private static final Hint hint = new ConditionHint(condition);
public Cactarantula(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.SPIDER);
this.power = new MageInt(6);
this.toughness = new MageInt(5);
// This spell costs {1} less to cast if you control a Desert.
this.addAbility(
new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition))
.setRuleAtTheTop(true)
.addHint(hint)
);
// Reach
this.addAbility(ReachAbility.getInstance());
// Whenever Cactarantula becomes the target of a spell or ability an opponent controls, you may draw a card.
this.addAbility(new BecomesTargetSourceTriggeredAbility(
new DrawCardSourceControllerEffect(1),
StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS,
SetTargetPointer.NONE, true
));
}
private Cactarantula(final Cactarantula card) {
super(card);
}
@Override
public Cactarantula copy() {
return new Cactarantula(this);
}
}

View file

@ -0,0 +1,79 @@
package mage.cards.c;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.CommanderGreatestManaValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect;
import mage.abilities.keyword.ReachAbility;
import mage.abilities.mana.AnyColorLandsProduceManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.token.custom.CreatureToken;
import java.util.UUID;
/**
* @author Susucr
*/
public final class CactusPreserve extends CardImpl {
public CactusPreserve(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.subtype.add(SubType.DESERT);
// Cactus Preserve enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility());
// {T}: Add one mana of any type that a land you control could produce.
this.addAbility(new AnyColorLandsProduceManaAbility(TargetController.YOU, false));
// {3}: Until end of turn, Cactus Preserve becomes an X/X green Plant creature with reach, where X is the greatest mana value among your commanders. It's still a land.
this.addAbility(new SimpleActivatedAbility(new CactusPreserveEffect(), new ManaCostsImpl<>("{3}")));
}
private CactusPreserve(final CactusPreserve card) {
super(card);
}
@Override
public CactusPreserve copy() {
return new CactusPreserve(this);
}
}
class CactusPreserveEffect extends OneShotEffect {
CactusPreserveEffect() {
super(Outcome.BecomeCreature);
this.staticText = "Until end of turn, {this} becomes an X/X green Plant creature with reach, "
+ "where X is the greatest mana value among your commanders. It's still a land.";
}
private CactusPreserveEffect(final CactusPreserveEffect effect) {
super(effect);
}
@Override
public CactusPreserveEffect copy() {
return new CactusPreserveEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int xValue = CommanderGreatestManaValue.instance.calculate(game, source, this);
game.addEffect(new BecomesCreatureSourceEffect(
new CreatureToken(xValue, xValue,
"X/X green Plant creature with reach, where X is the greatest mana value among your commanders")
.withColor("G").withSubType(SubType.PLANT)
.withAbility(ReachAbility.getInstance()),
CardType.LAND, Duration.EndOfTurn), source
);
return true;
}
}

View file

@ -0,0 +1,64 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.ReachAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.WardAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.PowerPredicate;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class CactusfolkSureshot extends CardImpl {
private static final FilterPermanent filter = new FilterCreaturePermanent();
static {
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
}
public CactusfolkSureshot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}");
this.subtype.add(SubType.PLANT);
this.subtype.add(SubType.MERCENARY);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Reach
this.addAbility(ReachAbility.getInstance());
// Ward {2}
this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}")));
// At the beginning of combat on your turn, other creatures you control with power 4 or greater gain trample and haste until end of turn.
Ability ability = new BeginningOfCombatTriggeredAbility(new GainAbilityControlledEffect(
TrampleAbility.getInstance(), Duration.EndOfTurn, filter, true
).setText("other creatures you control with power 4 or greater gain trample"), TargetController.YOU, false);
ability.addEffect(new GainAbilityControlledEffect(
HasteAbility.getInstance(), Duration.EndOfTurn, filter, true
).setText("and haste until end of turn"));
this.addAbility(ability);
}
private CactusfolkSureshot(final CactusfolkSureshot card) {
super(card);
}
@Override
public CactusfolkSureshot copy() {
return new CactusfolkSureshot(this);
}
}

View file

@ -0,0 +1,109 @@
package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksWhileSaddledTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.SaddleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.SaddledSourceThisTurnPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class CalamityGallopingInferno extends CardImpl {
public CalamityGallopingInferno(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HORSE);
this.subtype.add(SubType.MOUNT);
this.power = new MageInt(4);
this.toughness = new MageInt(6);
// Haste
this.addAbility(HasteAbility.getInstance());
// Whenever Calamity, Galloping Inferno attacks while saddled, choose a nonlegendary creature that saddled it this turn and create a tapped and attacking token that's a copy of it. Sacrifice that token at the beginning of the next end step. Repeat this process once.
this.addAbility(new AttacksWhileSaddledTriggeredAbility(new CalamityGallopingInfernoEffect()));
// Saddle 1
this.addAbility(new SaddleAbility(1));
}
private CalamityGallopingInferno(final CalamityGallopingInferno card) {
super(card);
}
@Override
public CalamityGallopingInferno copy() {
return new CalamityGallopingInferno(this);
}
}
class CalamityGallopingInfernoEffect extends OneShotEffect {
private static final FilterPermanent filter
= new FilterCreaturePermanent("nonlegendary creature that saddled it this turn");
static {
filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate()));
filter.add(SaddledSourceThisTurnPredicate.instance);
}
CalamityGallopingInfernoEffect() {
super(Outcome.Benefit);
staticText = "choose a nonlegendary creature that saddled it this turn " +
"and create a tapped and attacking token that's a copy of it. " +
"Sacrifice that token at the beginning of the next end step. Repeat this process once";
}
private CalamityGallopingInfernoEffect(final CalamityGallopingInfernoEffect effect) {
super(effect);
}
@Override
public CalamityGallopingInfernoEffect copy() {
return new CalamityGallopingInfernoEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null || !game.getBattlefield().contains(filter, source, game, 1)) {
return false;
}
for (int i = 0; i < 2; i++) {
TargetPermanent target = new TargetPermanent(filter);
target.withNotTarget(true);
player.choose(outcome, target, source, game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent == null) {
continue;
}
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(
null, null, false, 1, true, true
);
effect.apply(game, source);
effect.sacrificeTokensCreatedAtNextEndStep(game, source);
}
return true;
}
}

View file

@ -0,0 +1,51 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.AddCreatureTypeAdditionEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.counters.CounterType;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.target.common.TargetCardInYourGraveyard;
/**
* @author Cguy7777
*/
public final class CallASurpriseWitness extends CardImpl {
private static final FilterCreatureCard filter
= new FilterCreatureCard("creature card with mana value 3 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4));
}
public CallASurpriseWitness(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}");
// Return target creature card with mana value 3 or less from your graveyard to the battlefield.
// Put a flying counter on it. It's a Spirit in addition to its other types.
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.FLYING.createInstance())
.setText("put a flying counter on it"));
this.getSpellAbility().addEffect(new AddCreatureTypeAdditionEffect(SubType.SPIRIT, false)
.setText("it's a Spirit in addition to its other types"));
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter));
}
private CallASurpriseWitness(final CallASurpriseWitness card) {
super(card);
}
@Override
public CallASurpriseWitness copy() {
return new CallASurpriseWitness(this);
}
}

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